mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-04-01 00:00:03 +03:00
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
33483738c6 | ||
|
ea5bdadabd | ||
|
66c943be6c | ||
|
3d968816a4 | ||
|
76b3ec9d66 | ||
|
3e2d5b2b9a | ||
|
7c47145f6c | ||
|
396545c2ea | ||
|
3bfe433d22 | ||
|
19d3a1778c | ||
|
e54f288992 | ||
|
7c66e9fac5 | ||
|
2160e9a7bd | ||
|
941dfd7a29 | ||
|
18b4914587 | ||
|
8af5c93e48 | ||
|
6657ce2de3 | ||
|
5504533986 | ||
|
a55c655158 | ||
|
b8435c5693 | ||
|
80d0a21ed3 | ||
|
a1080f2f6a | ||
|
3bf9a19571 | ||
|
00dd889d33 | ||
|
1a8e684ae0 | ||
|
3d972489a8 | ||
|
197437be12 | ||
|
0a23a5ab1a | ||
|
017c3cc66e | ||
|
8b7810403b |
.bcachefs_revision
.github/workflows
Cargo.tomlMakefilebch_bindgen/src
c_src
bcachefs.ccmd_device.ccmd_format.ccmd_fs.ccmd_fsck.ccmd_fusemount.ccmd_list_journal.ccmd_migrate.ccmds.hlibbcachefs.clibbcachefs.hposix_to_bcachefs.cposix_to_bcachefs.h
flake.lockflake.nixinclude/linux
libbcachefs
alloc_background.calloc_foreground.calloc_foreground.hbackpointers.cbcachefs_format.hbtree_cache.cbtree_gc.cbtree_io.cbtree_iter.cbtree_iter.hbtree_journal_iter.cbtree_locking.cbtree_node_scan.cbtree_trans_commit.cbtree_update.cbtree_update.hbtree_update_interior.cbuckets.cchardev.cchecksum.ccompress.cdata_update.cdata_update.hdirent.cdirent.hdisk_accounting.cdisk_accounting.hdisk_accounting_format.hec.cec.herrcode.herror.cerror.hextents.cfs-io-buffered.cfs-io.cfs-ioctl.cfs.cfsck.cinode.cinode.hio_read.cio_read.hio_write.cio_write.hjournal.cjournal_io.cjournal_reclaim.clru.cmove.cmove_types.hnamei.cnamei.hopts.copts.hprintbuf.cprintbuf.hprogress.crebalance.crecovery.crecovery_passes.creflink.csb-errors_format.hsnapshot.cstr_hash.csubvolume.csuper.csysfs.ctime_stats.ctime_stats.hutil.cutil.h
src/commands
@ -1 +1 @@
|
||||
dbe591cee299957e282eb7857edea35050b1d8b5
|
||||
7fdc3fa3cb5fb561f5945b4de418d48d1a726a8d
|
||||
|
41
.github/workflows/nix-flake.yml
vendored
41
.github/workflows/nix-flake.yml
vendored
@ -1,22 +1,31 @@
|
||||
name: "Nix-Tests"
|
||||
name: Nix Flake actions
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
nix-flake-check:
|
||||
nix-matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v27
|
||||
with:
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: bcachefs-tools
|
||||
# If you chose API tokens for write access OR if you have a private cache
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- run: nix flake show
|
||||
- run: nix flake check --print-build-logs
|
||||
- run: nix build --print-build-logs
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
- id: set-matrix
|
||||
name: Generate Nix Matrix
|
||||
run: |
|
||||
set -Eeu
|
||||
matrix="$(nix eval --json '.#githubActions.matrix')"
|
||||
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
|
||||
|
||||
nix-build:
|
||||
name: ${{ matrix.name }} (${{ matrix.system }})
|
||||
needs: nix-matrix
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix: ${{fromJSON(needs.nix-matrix.outputs.matrix)}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
- run: nix build -L '.#${{ matrix.attr }}'
|
||||
|
@ -1,9 +1,13 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
default-members = [".", "bch_bindgen"]
|
||||
|
||||
[package]
|
||||
name = "bcachefs-tools"
|
||||
version = "1.25.0"
|
||||
authors = ["Yuxuan Shui <yshuiv7@gmail.com>", "Kayla Firestack <dev@kaylafire.me>", "Kent Overstreet <kent.overstreet@linux.dev>" ]
|
||||
edition = "2021"
|
||||
rust-version = "1.77"
|
||||
rust-version = "1.77.0"
|
||||
|
||||
[[bin]]
|
||||
name = "bcachefs"
|
||||
|
9
Makefile
9
Makefile
@ -22,6 +22,13 @@ else
|
||||
CARGO_CLEAN_ARGS = --quiet
|
||||
endif
|
||||
|
||||
# when cross compiling, cargo places the built binary in a different location
|
||||
ifdef CARGO_BUILD_TARGET
|
||||
BUILT_BIN = target/$(CARGO_BUILD_TARGET)/release/bcachefs
|
||||
else
|
||||
BUILT_BIN = target/release/bcachefs
|
||||
endif
|
||||
|
||||
# Prevent recursive expansions of $(CFLAGS) to avoid repeatedly performing
|
||||
# compile tests
|
||||
CFLAGS:=$(CFLAGS)
|
||||
@ -195,7 +202,7 @@ cmd_version.o : .version
|
||||
install: INITRAMFS_HOOK=$(INITRAMFS_DIR)/hooks/bcachefs
|
||||
install: INITRAMFS_SCRIPT=$(INITRAMFS_DIR)/scripts/local-premount/bcachefs
|
||||
install: bcachefs $(optional_install)
|
||||
$(INSTALL) -m0755 -D target/release/bcachefs -t $(DESTDIR)$(ROOT_SBINDIR)
|
||||
$(INSTALL) -m0755 -D $(BUILT_BIN) -t $(DESTDIR)$(ROOT_SBINDIR)
|
||||
$(INSTALL) -m0644 -D bcachefs.8 -t $(DESTDIR)$(PREFIX)/share/man/man8/
|
||||
$(INSTALL) -m0755 -D initramfs/script $(DESTDIR)$(INITRAMFS_SCRIPT)
|
||||
$(INSTALL) -m0755 -D initramfs/hook $(DESTDIR)$(INITRAMFS_HOOK)
|
||||
|
@ -1,3 +1,7 @@
|
||||
use crate::c;
|
||||
use crate::fs::Fs;
|
||||
use std::ffi::{CString, c_char};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! opt_set {
|
||||
($opts:ident, $n:ident, $v:expr) => {
|
||||
@ -33,3 +37,29 @@ macro_rules! opt_get {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn parse_mount_opts(fs: Option<&mut Fs>, optstr: Option<&str>, ignore_unknown: bool)
|
||||
-> Result<c::bch_opts, c::bch_errcode> {
|
||||
let mut opts: c::bch_opts = Default::default();
|
||||
|
||||
if let Some(optstr) = optstr {
|
||||
let optstr = CString::new(optstr).unwrap();
|
||||
let optstr_ptr = optstr.as_ptr();
|
||||
|
||||
let ret = unsafe {
|
||||
c::bch2_parse_mount_opts(fs.map_or(std::ptr::null_mut(), |f| f.raw),
|
||||
&mut opts as *mut c::bch_opts,
|
||||
std::ptr::null_mut(),
|
||||
optstr_ptr as *mut c_char,
|
||||
ignore_unknown)
|
||||
};
|
||||
|
||||
drop(optstr);
|
||||
|
||||
if ret != 0 {
|
||||
let err: c::bch_errcode = unsafe { std::mem::transmute(-ret) };
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Ok(opts)
|
||||
}
|
||||
|
@ -93,9 +93,11 @@ void bcachefs_usage(void)
|
||||
" list List filesystem metadata in textual form\n"
|
||||
" list_journal List contents of journal\n"
|
||||
"\n"
|
||||
#ifdef BCACHEFS_FUSE
|
||||
"FUSE:\n"
|
||||
" fusemount Mount a filesystem via FUSE\n"
|
||||
"\n"
|
||||
#endif
|
||||
"Miscellaneous:\n"
|
||||
" completions Generate shell completions\n"
|
||||
" version Display the version of the invoked bcachefs tool\n");
|
||||
@ -116,16 +118,15 @@ int fs_cmds(int argc, char *argv[])
|
||||
{
|
||||
char *cmd = pop_cmd(&argc, argv);
|
||||
|
||||
if (argc < 1) {
|
||||
bcachefs_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (argc < 1)
|
||||
return fs_usage();
|
||||
if (!strcmp(cmd, "usage"))
|
||||
return cmd_fs_usage(argc, argv);
|
||||
if (!strcmp(cmd, "top"))
|
||||
return cmd_fs_top(argc, argv);
|
||||
|
||||
return 0;
|
||||
fs_usage();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int device_cmds(int argc, char *argv[])
|
||||
@ -151,7 +152,8 @@ int device_cmds(int argc, char *argv[])
|
||||
if (!strcmp(cmd, "resize-journal"))
|
||||
return cmd_device_resize_journal(argc, argv);
|
||||
|
||||
return 0;
|
||||
device_usage();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int data_cmds(int argc, char *argv[])
|
||||
@ -167,5 +169,6 @@ int data_cmds(int argc, char *argv[])
|
||||
if (!strcmp(cmd, "job"))
|
||||
return cmd_data_job(argc, argv);
|
||||
|
||||
return 0;
|
||||
data_usage();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -137,10 +137,11 @@ int cmd_device_add(int argc, char *argv[])
|
||||
opt_set(fs_opts, btree_node_size,
|
||||
read_file_u64(fs.sysfs_fd, "options/btree_node_size"));
|
||||
|
||||
struct bch_sb *sb = bch2_format(fs_opt_strs,
|
||||
fs_opts,
|
||||
format_opts,
|
||||
&dev_opts, 1);
|
||||
dev_opts_list devs = {};
|
||||
darray_push(&devs, dev_opts);
|
||||
|
||||
struct bch_sb *sb = bch2_format(fs_opt_strs, fs_opts, format_opts, devs);
|
||||
darray_exit(&devs);
|
||||
free(sb);
|
||||
bchu_disk_add(fs, dev_opts.path);
|
||||
return 0;
|
||||
|
@ -121,13 +121,13 @@ void build_fs(struct bch_fs *c, const char *src_path)
|
||||
if (!S_ISDIR(stat.st_mode))
|
||||
die("%s is not a directory", src_path);
|
||||
|
||||
copy_fs(c, src_fd, src_path, &s);
|
||||
copy_fs(c, src_fd, src_path, &s, 0);
|
||||
}
|
||||
|
||||
int cmd_format(int argc, char *argv[])
|
||||
{
|
||||
DARRAY(struct dev_opts) devices = { 0 };
|
||||
DARRAY(char *) device_paths = { 0 };
|
||||
dev_opts_list devices = {};
|
||||
darray_str device_paths = {};
|
||||
struct format_opts opts = format_opts_default();
|
||||
struct dev_opts dev_opts = dev_opts_default();
|
||||
bool force = false, no_passphrase = false, quiet = false, initialize = true, verbose = false;
|
||||
@ -207,9 +207,8 @@ int cmd_format(int argc, char *argv[])
|
||||
force = true;
|
||||
break;
|
||||
case O_fs_size:
|
||||
if (bch2_strtoull_h(optarg, &dev_opts.opts.fs_size))
|
||||
if (bch2_strtoull_h(optarg, &dev_opts.fs_size))
|
||||
die("invalid filesystem size");
|
||||
dev_opts.opts.fs_size_defined = true;
|
||||
unconsumed_dev_option = true;
|
||||
break;
|
||||
case O_superblock_size:
|
||||
@ -233,8 +232,7 @@ int cmd_format(int argc, char *argv[])
|
||||
darray_push(&device_paths, optarg);
|
||||
dev_opts.path = optarg;
|
||||
darray_push(&devices, dev_opts);
|
||||
dev_opts.opts.fs_size = 0;
|
||||
dev_opts.opts.fs_size_defined = 0;
|
||||
dev_opts.fs_size = 0;
|
||||
unconsumed_dev_option = false;
|
||||
break;
|
||||
case O_quiet:
|
||||
@ -277,11 +275,7 @@ int cmd_format(int argc, char *argv[])
|
||||
die("Error opening %s: %s", dev_opts.path, strerror(-ret));
|
||||
}
|
||||
|
||||
struct bch_sb *sb =
|
||||
bch2_format(fs_opt_strs,
|
||||
fs_opts,
|
||||
opts,
|
||||
devices.data, devices.nr);
|
||||
struct bch_sb *sb = bch2_format(fs_opt_strs, fs_opts, opts, devices);
|
||||
bch2_opt_strs_free(&fs_opt_strs);
|
||||
|
||||
if (!quiet) {
|
||||
|
@ -239,9 +239,10 @@ static void accounting_sort(darray_accounting_p *sorted,
|
||||
|
||||
static void accounting_swab_if_old(struct bch_ioctl_query_accounting *in)
|
||||
{
|
||||
u64 kernel_version = read_file_u64(AT_FDCWD, "/sys/module/bcachefs/parameters/version");
|
||||
unsigned kernel_version = bcachefs_kernel_version();
|
||||
|
||||
if (kernel_version < bcachefs_metadata_version_disk_accounting_big_endian)
|
||||
if (kernel_version &&
|
||||
kernel_version < bcachefs_metadata_version_disk_accounting_big_endian)
|
||||
for (struct bkey_i_accounting *a = in->accounting;
|
||||
a < (struct bkey_i_accounting *) ((u64 *) in->accounting + in->accounting_u64s);
|
||||
a = bkey_i_to_accounting(bkey_next(&a->k_i)))
|
||||
@ -490,6 +491,19 @@ devs:
|
||||
bcache_fs_close(fs);
|
||||
}
|
||||
|
||||
int fs_usage(void)
|
||||
{
|
||||
puts("bcachefs fs - manage a running filesystem\n"
|
||||
"Usage: bcachefs fs <CMD> [OPTIONS]\n"
|
||||
"\n"
|
||||
"Commands:\n"
|
||||
" usage Display detailed filesystem usage\n"
|
||||
" top Show runtime performance information\n"
|
||||
"\n"
|
||||
"Report bugs to <linux-bcachefs@vger.kernel.org>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fs_usage_usage(void)
|
||||
{
|
||||
puts("bcachefs fs usage - display detailed filesystem usage\n"
|
||||
|
@ -21,7 +21,6 @@ static void fsck_usage(void)
|
||||
" -y Assume \"yes\" to all questions\n"
|
||||
" -f Force checking even if filesystem is marked clean\n"
|
||||
" -r, --ratelimit_errors Don't display more than 10 errors of a given type\n"
|
||||
" -R, --reconstruct_alloc Reconstruct the alloc btree\n"
|
||||
" -k, --kernel Use the in-kernel fsck implementation\n"
|
||||
" -v Be verbose\n"
|
||||
" -h, --help Display this help and exit\n"
|
||||
@ -117,9 +116,7 @@ static bool should_use_kernel_fsck(darray_str devs)
|
||||
{
|
||||
system("modprobe bcachefs");
|
||||
|
||||
unsigned kernel_version = !access("/sys/module/bcachefs/parameters/version", R_OK)
|
||||
? read_file_u64(AT_FDCWD, "/sys/module/bcachefs/parameters/version")
|
||||
: 0;
|
||||
unsigned kernel_version = bcachefs_kernel_version();
|
||||
|
||||
if (!kernel_version)
|
||||
return false;
|
||||
@ -205,7 +202,6 @@ int cmd_fsck(int argc, char *argv[])
|
||||
{
|
||||
static const struct option longopts[] = {
|
||||
{ "ratelimit_errors", no_argument, NULL, 'r' },
|
||||
{ "reconstruct_alloc", no_argument, NULL, 'R' },
|
||||
{ "kernel", no_argument, NULL, 'k' },
|
||||
{ "no-kernel", no_argument, NULL, 'K' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
@ -224,7 +220,7 @@ int cmd_fsck(int argc, char *argv[])
|
||||
append_opt(&opts_str, "read_only");
|
||||
|
||||
while ((opt = getopt_long(argc, argv,
|
||||
"apynfo:rRkKvh",
|
||||
"apynfo:rkKvh",
|
||||
longopts, NULL)) != -1)
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
@ -248,9 +244,6 @@ int cmd_fsck(int argc, char *argv[])
|
||||
case 'r':
|
||||
append_opt(&opts_str, "ratelimit_errors");
|
||||
break;
|
||||
case 'R':
|
||||
append_opt(&opts_str, "reconstruct_alloc");
|
||||
break;
|
||||
case 'k':
|
||||
kernel = true;
|
||||
break;
|
||||
@ -326,7 +319,7 @@ kernel_fsck_err:
|
||||
} else {
|
||||
userland_fsck:
|
||||
printf("Running userspace offline fsck\n");
|
||||
ret = bch2_parse_mount_opts(NULL, &opts, &parse_later, opts_str.buf);
|
||||
ret = bch2_parse_mount_opts(NULL, &opts, &parse_later, opts_str.buf, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "libbcachefs/dirent.h"
|
||||
#include "libbcachefs/errcode.h"
|
||||
#include "libbcachefs/error.h"
|
||||
#include "libbcachefs/fs-common.h"
|
||||
#include "libbcachefs/namei.h"
|
||||
#include "libbcachefs/inode.h"
|
||||
#include "libbcachefs/io_read.h"
|
||||
#include "libbcachefs/io_write.h"
|
||||
|
@ -65,9 +65,21 @@ static bool bkey_matches_filter(d_bbpos_range filter, struct jset_entry *entry,
|
||||
struct bbpos k_start = BBPOS(entry->btree_id, bkey_start_pos(&k->k));
|
||||
struct bbpos k_end = BBPOS(entry->btree_id, k->k.p);
|
||||
|
||||
if (bbpos_cmp(k_start, i->start) >= 0 &&
|
||||
bbpos_cmp(k_end, i->end) <= 0)
|
||||
return true;
|
||||
if (!i->start.pos.snapshot &&
|
||||
!i->end.pos.snapshot) {
|
||||
k_start.pos.snapshot = 0;
|
||||
k_end.pos.snapshot = 0;
|
||||
}
|
||||
|
||||
if (!k->k.size) {
|
||||
if (bbpos_cmp(k_start, i->start) >= 0 &&
|
||||
bbpos_cmp(k_end, i->end) <= 0)
|
||||
return true;
|
||||
} else {
|
||||
if (bbpos_cmp(i->start, k_end) <= 0 &&
|
||||
bbpos_cmp(i->end, k_start) >= 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -75,9 +87,9 @@ static bool bkey_matches_filter(d_bbpos_range filter, struct jset_entry *entry,
|
||||
static bool entry_matches_transaction_filter(struct jset_entry *entry,
|
||||
d_bbpos_range filter)
|
||||
{
|
||||
if (entry->type == BCH_JSET_ENTRY_btree_root ||
|
||||
entry->type == BCH_JSET_ENTRY_btree_keys ||
|
||||
entry->type == BCH_JSET_ENTRY_overwrite)
|
||||
if (!entry->level &&
|
||||
(entry->type == BCH_JSET_ENTRY_btree_keys ||
|
||||
entry->type == BCH_JSET_ENTRY_overwrite))
|
||||
jset_entry_for_each_key(entry, k)
|
||||
if (bkey_matches_filter(filter, entry, k))
|
||||
return true;
|
||||
@ -90,6 +102,8 @@ static bool should_print_transaction(struct jset_entry *entry, struct jset_entry
|
||||
{
|
||||
struct jset_entry_log *l = container_of(entry, struct jset_entry_log, entry);
|
||||
unsigned b = jset_entry_log_msg_bytes(l);
|
||||
bool have_log_messages = false;
|
||||
bool have_non_log_messages = false;
|
||||
|
||||
darray_for_each(msg_filter, i)
|
||||
if (!strncmp(*i, l->d, b))
|
||||
@ -100,11 +114,19 @@ static bool should_print_transaction(struct jset_entry *entry, struct jset_entry
|
||||
|
||||
for (entry = vstruct_next(entry);
|
||||
entry != end && !entry_is_transaction_start(entry);
|
||||
entry = vstruct_next(entry))
|
||||
if (entry_is_log_msg(entry) ||
|
||||
entry_matches_transaction_filter(entry, key_filter))
|
||||
entry = vstruct_next(entry)) {
|
||||
if (entry_matches_transaction_filter(entry, key_filter))
|
||||
return true;
|
||||
|
||||
if (entry_is_log_msg(entry))
|
||||
have_log_messages = true;
|
||||
else
|
||||
have_non_log_messages = true;
|
||||
}
|
||||
|
||||
if (have_log_messages && !have_non_log_messages)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -134,6 +156,7 @@ static void journal_entry_header_to_text(struct printbuf *out,
|
||||
prt_str(out, "blacklisted ");
|
||||
|
||||
prt_printf(out,
|
||||
"\n"
|
||||
"journal entry %llu\n"
|
||||
" version %u\n"
|
||||
" last seq %llu\n"
|
||||
|
@ -209,38 +209,43 @@ static int migrate_fs(const char *fs_path,
|
||||
if (!S_ISDIR(stat.st_mode))
|
||||
die("%s is not a directory", fs_path);
|
||||
|
||||
struct dev_opts dev = dev_opts_default();
|
||||
dev_opts_list devs = {};
|
||||
darray_push(&devs, dev_opts_default());
|
||||
|
||||
dev.path = dev_t_to_path(stat.st_dev);
|
||||
dev.file = bdev_file_open_by_path(dev.path, BLK_OPEN_READ|BLK_OPEN_WRITE, &dev, NULL);
|
||||
struct dev_opts *dev = &devs.data[0];
|
||||
|
||||
int ret = PTR_ERR_OR_ZERO(dev.file);
|
||||
dev->path = dev_t_to_path(stat.st_dev);
|
||||
dev->file = bdev_file_open_by_path(dev->path, BLK_OPEN_READ|BLK_OPEN_WRITE, dev, NULL);
|
||||
|
||||
int ret = PTR_ERR_OR_ZERO(dev->file);
|
||||
if (ret < 0)
|
||||
die("Error opening device to format %s: %s", dev.path, strerror(-ret));
|
||||
dev.bdev = file_bdev(dev.file);
|
||||
die("Error opening device to format %s: %s", dev->path, strerror(-ret));
|
||||
dev->bdev = file_bdev(dev->file);
|
||||
|
||||
opt_set(fs_opts, block_size, get_blocksize(dev.bdev->bd_fd));
|
||||
opt_set(fs_opts, block_size, get_blocksize(dev->bdev->bd_fd));
|
||||
|
||||
char *file_path = mprintf("%s/bcachefs", fs_path);
|
||||
printf("Creating new filesystem on %s in space reserved at %s\n",
|
||||
dev.path, file_path);
|
||||
dev->path, file_path);
|
||||
|
||||
dev.opts.fs_size = get_size(dev.bdev->bd_fd);
|
||||
dev.opts.bucket_size = bch2_pick_bucket_size(fs_opts, &dev);
|
||||
dev.nbuckets = dev.opts.fs_size / dev.opts.bucket_size;
|
||||
dev->fs_size = get_size(dev->bdev->bd_fd);
|
||||
opt_set(dev->opts, bucket_size, bch2_pick_bucket_size(fs_opts, devs));
|
||||
|
||||
bch2_check_bucket_size(fs_opts, &dev);
|
||||
dev->nbuckets = dev->fs_size / dev->opts.bucket_size;
|
||||
|
||||
bch2_check_bucket_size(fs_opts, dev);
|
||||
|
||||
u64 bcachefs_inum;
|
||||
ranges extents = reserve_new_fs_space(file_path,
|
||||
fs_opts.block_size >> 9,
|
||||
get_size(dev.bdev->bd_fd) / 5,
|
||||
get_size(dev->bdev->bd_fd) / 5,
|
||||
&bcachefs_inum, stat.st_dev, force);
|
||||
|
||||
find_superblock_space(extents, format_opts, &dev);
|
||||
find_superblock_space(extents, format_opts, dev);
|
||||
|
||||
struct bch_sb *sb = bch2_format(fs_opt_strs, fs_opts, format_opts, devs);
|
||||
darray_exit(&devs);
|
||||
|
||||
struct bch_sb *sb = bch2_format(fs_opt_strs,
|
||||
fs_opts, format_opts, &dev, 1);
|
||||
u64 sb_offset = le64_to_cpu(sb->layout.sb_offset[0]);
|
||||
|
||||
if (format_opts.passphrase)
|
||||
@ -248,16 +253,14 @@ static int migrate_fs(const char *fs_path,
|
||||
|
||||
free(sb);
|
||||
|
||||
struct bch_opts opts = bch2_opts_empty();
|
||||
struct bch_fs *c = NULL;
|
||||
char *path[1] = { dev.path };
|
||||
char *path[1] = { dev->path };
|
||||
|
||||
struct bch_opts opts = bch2_opts_empty();
|
||||
opt_set(opts, sb, sb_offset);
|
||||
opt_set(opts, nostart, true);
|
||||
opt_set(opts, noexcl, true);
|
||||
opt_set(opts, nostart, true);
|
||||
|
||||
c = bch2_fs_open(path, 1, opts);
|
||||
struct bch_fs *c = bch2_fs_open(path, 1, opts);
|
||||
if (IS_ERR(c))
|
||||
die("Error opening new filesystem: %s", bch2_err_str(PTR_ERR(c)));
|
||||
|
||||
@ -265,10 +268,6 @@ static int migrate_fs(const char *fs_path,
|
||||
if (ret)
|
||||
die("Error allocating buckets_nouse: %s", bch2_err_str(ret));
|
||||
|
||||
ret = bch2_fs_start(c);
|
||||
if (IS_ERR(c))
|
||||
die("Error starting new filesystem: %s", bch2_err_str(ret));
|
||||
|
||||
mark_unreserved_space(c, extents);
|
||||
|
||||
ret = bch2_fs_start(c);
|
||||
@ -282,7 +281,10 @@ static int migrate_fs(const char *fs_path,
|
||||
.type = BCH_MIGRATE_migrate,
|
||||
};
|
||||
|
||||
copy_fs(c, fs_fd, fs_path, &s);
|
||||
u64 reserve_start = round_up((format_opts.superblock_size * 2 + 8) << 9,
|
||||
dev->opts.bucket_size);
|
||||
|
||||
copy_fs(c, fs_fd, fs_path, &s, reserve_start);
|
||||
|
||||
bch2_fs_stop(c);
|
||||
|
||||
@ -310,7 +312,7 @@ static int migrate_fs(const char *fs_path,
|
||||
"filesystem. That file can be deleted once the old filesystem is\n"
|
||||
"no longer needed (and should be deleted prior to running\n"
|
||||
"bcachefs migrate-superblock)\n",
|
||||
sb_offset, dev.path, dev.path, sb_offset);
|
||||
sb_offset, dev->path, dev->path, sb_offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -374,7 +376,7 @@ static void migrate_superblock_usage(void)
|
||||
int cmd_migrate_superblock(int argc, char *argv[])
|
||||
{
|
||||
char *dev = NULL;
|
||||
u64 offset = 0;
|
||||
u64 sb_offset = 0;
|
||||
int opt, ret;
|
||||
|
||||
while ((opt = getopt(argc, argv, "d:o:h")) != -1)
|
||||
@ -383,7 +385,7 @@ int cmd_migrate_superblock(int argc, char *argv[])
|
||||
dev = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
ret = kstrtou64(optarg, 10, &offset);
|
||||
ret = kstrtou64(optarg, 10, &sb_offset);
|
||||
if (ret)
|
||||
die("Invalid offset");
|
||||
break;
|
||||
@ -395,29 +397,72 @@ int cmd_migrate_superblock(int argc, char *argv[])
|
||||
if (!dev)
|
||||
die("Please specify a device");
|
||||
|
||||
if (!offset)
|
||||
if (!sb_offset)
|
||||
die("Please specify offset of existing superblock");
|
||||
|
||||
int fd = xopen(dev, O_RDWR);
|
||||
struct bch_sb *sb = __bch2_super_read(fd, offset);
|
||||
struct bch_sb *sb = __bch2_super_read(fd, sb_offset);
|
||||
unsigned sb_size = 1U << sb->layout.sb_max_size_bits;
|
||||
|
||||
if (sb->layout.nr_superblocks >= ARRAY_SIZE(sb->layout.sb_offset))
|
||||
die("Can't add superblock: no space left in superblock layout");
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < sb->layout.nr_superblocks; i++)
|
||||
if (le64_to_cpu(sb->layout.sb_offset[i]) == BCH_SB_SECTOR)
|
||||
die("Superblock layout already has default superblock");
|
||||
for (unsigned i = 0; i < sb->layout.nr_superblocks; i++)
|
||||
if (le64_to_cpu(sb->layout.sb_offset[i]) == BCH_SB_SECTOR ||
|
||||
le64_to_cpu(sb->layout.sb_offset[i]) == BCH_SB_SECTOR + sb_size)
|
||||
die("Superblock layout already has default superblocks");
|
||||
|
||||
memmove(&sb->layout.sb_offset[1],
|
||||
memmove(&sb->layout.sb_offset[2],
|
||||
&sb->layout.sb_offset[0],
|
||||
sb->layout.nr_superblocks * sizeof(u64));
|
||||
sb->layout.nr_superblocks++;
|
||||
|
||||
sb->layout.nr_superblocks += 2;
|
||||
sb->layout.sb_offset[0] = cpu_to_le64(BCH_SB_SECTOR);
|
||||
sb->layout.sb_offset[1] = cpu_to_le64(BCH_SB_SECTOR + sb_size);
|
||||
|
||||
/* also write first 0-3.5k bytes with zeroes, ensure we blow away old
|
||||
* superblock */
|
||||
static const char zeroes[BCH_SB_SECTOR << 9];
|
||||
xpwrite(fd, zeroes, BCH_SB_SECTOR << 9, 0, "zeroing start of disk");
|
||||
|
||||
bch2_super_write(fd, sb);
|
||||
close(fd);
|
||||
|
||||
/* mark new superblocks */
|
||||
|
||||
struct bch_opts opts = bch2_opts_empty();
|
||||
opt_set(opts, nostart, true);
|
||||
opt_set(opts, sb, sb_offset);
|
||||
|
||||
struct bch_fs *c = bch2_fs_open(&dev, 1, opts);
|
||||
ret = PTR_ERR_OR_ZERO(c) ?:
|
||||
bch2_buckets_nouse_alloc(c);
|
||||
if (ret)
|
||||
die("error opening filesystem: %s", bch2_err_str(ret));
|
||||
|
||||
struct bch_dev *ca = c->devs[0];
|
||||
for (u64 b = 0; bucket_to_sector(ca, b) < BCH_SB_SECTOR + sb_size * 2; b++)
|
||||
set_bit(b, ca->buckets_nouse);
|
||||
|
||||
ret = bch2_fs_start(c);
|
||||
if (ret)
|
||||
die("Error starting filesystem: %s", bch2_err_str(ret));
|
||||
|
||||
bch2_fs_stop(c);
|
||||
|
||||
opts = bch2_opts_empty();
|
||||
opt_set(opts, fsck, true);
|
||||
opt_set(opts, fix_errors, true);
|
||||
|
||||
/*
|
||||
* Hack: the free space counters are coming out wrong after marking the
|
||||
* new superblock, but it's just the device counters so it's
|
||||
* inconsequential:
|
||||
*/
|
||||
|
||||
c = bch2_fs_open(&dev, 1, opts);
|
||||
ret = PTR_ERR_OR_ZERO(c);
|
||||
if (ret)
|
||||
die("error opening filesystem: %s", bch2_err_str(ret));
|
||||
bch2_fs_stop(c);
|
||||
return 0;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ int cmd_recover_super(int argc, char *argv[]);
|
||||
int cmd_reset_counters(int argc, char *argv[]);
|
||||
int cmd_set_option(int argc, char *argv[]);
|
||||
|
||||
int fs_usage(void);
|
||||
int cmd_fs_usage(int argc, char *argv[]);
|
||||
int cmd_fs_top(int argc, char *argv[]);
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include "libbcachefs.h"
|
||||
#include "crypto.h"
|
||||
#include "libbcachefs/bcachefs_format.h"
|
||||
@ -60,41 +62,54 @@ void bch2_sb_layout_init(struct bch_sb_layout *l,
|
||||
sb_start, sb_pos, sb_end, sb_size);
|
||||
}
|
||||
|
||||
/* minimum size filesystem we can create, given a bucket size: */
|
||||
static u64 min_size(unsigned bucket_size)
|
||||
static u64 dev_max_bucket_size(u64 dev_size)
|
||||
{
|
||||
return BCH_MIN_NR_NBUCKETS * bucket_size;
|
||||
return rounddown_pow_of_two(dev_size / (BCH_MIN_NR_NBUCKETS * 4));
|
||||
}
|
||||
|
||||
u64 bch2_pick_bucket_size(struct bch_opts opts, struct dev_opts *dev)
|
||||
u64 bch2_pick_bucket_size(struct bch_opts opts, dev_opts_list devs)
|
||||
{
|
||||
if (dev->opts.fs_size < min_size(opts.block_size))
|
||||
die("cannot format %s, too small (%llu bytes, min %llu)",
|
||||
dev->path, dev->opts.fs_size, min_size(opts.block_size));
|
||||
|
||||
/* Bucket size must be >= block size: */
|
||||
u64 bucket_size = opts.block_size;
|
||||
|
||||
/* Bucket size must be >= btree node size: */
|
||||
if (opt_defined(opts, btree_node_size))
|
||||
bucket_size = max_t(unsigned, bucket_size, opts.btree_node_size);
|
||||
bucket_size = max_t(u64, bucket_size, opts.btree_node_size);
|
||||
|
||||
/* Want a bucket size of at least 128k, if possible: */
|
||||
bucket_size = max(bucket_size, 128ULL << 10);
|
||||
u64 min_dev_size = BCH_MIN_NR_NBUCKETS * bucket_size;
|
||||
darray_for_each(devs, i)
|
||||
if (i->fs_size < min_dev_size)
|
||||
die("cannot format %s, too small (%llu bytes, min %llu)",
|
||||
i->path, i->fs_size, min_dev_size);
|
||||
|
||||
if (dev->opts.fs_size >= min_size(bucket_size)) {
|
||||
unsigned scale = max(1,
|
||||
ilog2(dev->opts.fs_size / min_size(bucket_size)) / 4);
|
||||
u64 total_fs_size = 0;
|
||||
darray_for_each(devs, i)
|
||||
total_fs_size += i->fs_size;
|
||||
|
||||
scale = rounddown_pow_of_two(scale);
|
||||
struct sysinfo info;
|
||||
si_meminfo(&info);
|
||||
|
||||
/* max bucket size 1 mb */
|
||||
bucket_size = min(bucket_size * scale, 1ULL << 20);
|
||||
} else {
|
||||
do {
|
||||
bucket_size /= 2;
|
||||
} while (dev->opts.fs_size < min_size(bucket_size));
|
||||
}
|
||||
/*
|
||||
* Large fudge factor to allow for other fsck processes and devices
|
||||
* being added after creation
|
||||
*/
|
||||
u64 mem_available_for_fsck = info.totalram / 8;
|
||||
u64 buckets_can_fsck = mem_available_for_fsck / (sizeof(struct bucket) * 1.5);
|
||||
u64 mem_lower_bound = roundup_pow_of_two(total_fs_size / buckets_can_fsck);
|
||||
|
||||
/*
|
||||
* Lower bound to avoid fragmenting encoded (checksummed, compressed)
|
||||
* extents too much as they're moved:
|
||||
*/
|
||||
bucket_size = max(bucket_size, opt_get(opts, encoded_extent_max) * 4);
|
||||
|
||||
/* Lower bound to ensure we can fsck: */
|
||||
bucket_size = max(bucket_size, mem_lower_bound);
|
||||
|
||||
u64 perf_lower_bound = min(2ULL << 20, total_fs_size / (1ULL << 20));
|
||||
|
||||
/* We also prefer larger buckets for performance, up to 2MB at 2T */
|
||||
bucket_size = max(bucket_size, perf_lower_bound);
|
||||
|
||||
return bucket_size;
|
||||
}
|
||||
@ -116,20 +131,17 @@ void bch2_check_bucket_size(struct bch_opts opts, struct dev_opts *dev)
|
||||
}
|
||||
|
||||
static unsigned parse_target(struct bch_sb_handle *sb,
|
||||
struct dev_opts *devs, size_t nr_devs,
|
||||
dev_opts_list devs,
|
||||
const char *s)
|
||||
{
|
||||
struct dev_opts *i;
|
||||
int idx;
|
||||
|
||||
if (!s)
|
||||
return 0;
|
||||
|
||||
for (i = devs; i < devs + nr_devs; i++)
|
||||
darray_for_each(devs, i)
|
||||
if (!strcmp(s, i->path))
|
||||
return dev_to_target(i - devs);
|
||||
return dev_to_target(i - devs.data);
|
||||
|
||||
idx = bch2_disk_path_find(sb, s);
|
||||
int idx = bch2_disk_path_find(sb, s);
|
||||
if (idx >= 0)
|
||||
return group_to_target(idx);
|
||||
|
||||
@ -151,15 +163,12 @@ static void bch2_opt_set_sb_all(struct bch_sb *sb, int dev_idx, struct bch_opts
|
||||
struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs,
|
||||
struct bch_opts fs_opts,
|
||||
struct format_opts opts,
|
||||
struct dev_opts *devs,
|
||||
size_t nr_devs)
|
||||
dev_opts_list devs)
|
||||
{
|
||||
struct bch_sb_handle sb = { NULL };
|
||||
struct dev_opts *i;
|
||||
unsigned max_dev_block_size = 0;
|
||||
u64 min_bucket_size = U64_MAX;
|
||||
|
||||
for (i = devs; i < devs + nr_devs; i++)
|
||||
darray_for_each(devs, i)
|
||||
max_dev_block_size = max(max_dev_block_size, get_blocksize(i->bdev->bd_fd));
|
||||
|
||||
/* calculate block size: */
|
||||
@ -171,21 +180,20 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs,
|
||||
fs_opts.block_size, max_dev_block_size);
|
||||
|
||||
/* get device size, if it wasn't specified: */
|
||||
for (i = devs; i < devs + nr_devs; i++)
|
||||
if (!opt_defined(i->opts, fs_size))
|
||||
opt_set(i->opts, fs_size, get_size(i->bdev->bd_fd));
|
||||
darray_for_each(devs, i)
|
||||
if (!i->fs_size)
|
||||
i->fs_size = get_size(i->bdev->bd_fd);
|
||||
|
||||
/* calculate bucket sizes: */
|
||||
for (i = devs; i < devs + nr_devs; i++)
|
||||
min_bucket_size = min(min_bucket_size,
|
||||
i->opts.bucket_size ?: bch2_pick_bucket_size(fs_opts, i));
|
||||
u64 fs_bucket_size = bch2_pick_bucket_size(fs_opts, devs);
|
||||
|
||||
for (i = devs; i < devs + nr_devs; i++)
|
||||
darray_for_each(devs, i)
|
||||
if (!opt_defined(i->opts, bucket_size))
|
||||
opt_set(i->opts, bucket_size, min_bucket_size);
|
||||
opt_set(i->opts, bucket_size,
|
||||
min(fs_bucket_size, dev_max_bucket_size(i->fs_size)));
|
||||
|
||||
for (i = devs; i < devs + nr_devs; i++) {
|
||||
i->nbuckets = i->opts.fs_size / i->opts.bucket_size;
|
||||
darray_for_each(devs, i) {
|
||||
i->nbuckets = i->fs_size / i->opts.bucket_size;
|
||||
bch2_check_bucket_size(fs_opts, i);
|
||||
}
|
||||
|
||||
@ -193,7 +201,7 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs,
|
||||
if (!opt_defined(fs_opts, btree_node_size)) {
|
||||
unsigned s = bch2_opts_default.btree_node_size;
|
||||
|
||||
for (i = devs; i < devs + nr_devs; i++)
|
||||
darray_for_each(devs, i)
|
||||
s = min(s, i->opts.bucket_size);
|
||||
opt_set(fs_opts, btree_node_size, s);
|
||||
}
|
||||
@ -208,7 +216,7 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs,
|
||||
sb.sb->version_min = le16_to_cpu(opts.version);
|
||||
sb.sb->magic = BCHFS_MAGIC;
|
||||
sb.sb->user_uuid = opts.uuid;
|
||||
sb.sb->nr_devices = nr_devs;
|
||||
sb.sb->nr_devices = devs.nr;
|
||||
SET_BCH_SB_VERSION_INCOMPAT_ALLOWED(sb.sb, opts.version);
|
||||
|
||||
if (opts.version == bcachefs_metadata_version_current)
|
||||
@ -233,11 +241,11 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs,
|
||||
/* Member info: */
|
||||
struct bch_sb_field_members_v2 *mi =
|
||||
bch2_sb_field_resize(&sb, members_v2,
|
||||
(sizeof(*mi) + sizeof(struct bch_member) *
|
||||
nr_devs) / sizeof(u64));
|
||||
(sizeof(*mi) + sizeof(struct bch_member) * devs.nr) / sizeof(u64));
|
||||
|
||||
mi->member_bytes = cpu_to_le16(sizeof(struct bch_member));
|
||||
for (i = devs; i < devs + nr_devs; i++) {
|
||||
unsigned idx = i - devs;
|
||||
darray_for_each(devs, i) {
|
||||
unsigned idx = i - devs.data;
|
||||
struct bch_member *m = bch2_members_v2_get_mut(sb.sb, idx);
|
||||
|
||||
uuid_generate(m->uuid.b);
|
||||
@ -247,14 +255,11 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs,
|
||||
}
|
||||
|
||||
/* Disk labels*/
|
||||
for (i = devs; i < devs + nr_devs; i++) {
|
||||
struct bch_member *m;
|
||||
int idx;
|
||||
|
||||
darray_for_each(devs, i) {
|
||||
if (!i->label)
|
||||
continue;
|
||||
|
||||
idx = bch2_disk_path_find_or_create(&sb, i->label);
|
||||
int idx = bch2_disk_path_find_or_create(&sb, i->label);
|
||||
if (idx < 0)
|
||||
die("error creating disk path: %s", strerror(-idx));
|
||||
|
||||
@ -262,18 +267,18 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs,
|
||||
* Recompute mi and m after each sb modification: its location
|
||||
* in memory may have changed due to reallocation.
|
||||
*/
|
||||
m = bch2_members_v2_get_mut(sb.sb, (i - devs));
|
||||
struct bch_member *m = bch2_members_v2_get_mut(sb.sb, (i - devs.data));
|
||||
SET_BCH_MEMBER_GROUP(m, idx + 1);
|
||||
}
|
||||
|
||||
SET_BCH_SB_FOREGROUND_TARGET(sb.sb,
|
||||
parse_target(&sb, devs, nr_devs, fs_opt_strs.foreground_target));
|
||||
parse_target(&sb, devs, fs_opt_strs.foreground_target));
|
||||
SET_BCH_SB_BACKGROUND_TARGET(sb.sb,
|
||||
parse_target(&sb, devs, nr_devs, fs_opt_strs.background_target));
|
||||
parse_target(&sb, devs, fs_opt_strs.background_target));
|
||||
SET_BCH_SB_PROMOTE_TARGET(sb.sb,
|
||||
parse_target(&sb, devs, nr_devs, fs_opt_strs.promote_target));
|
||||
parse_target(&sb, devs, fs_opt_strs.promote_target));
|
||||
SET_BCH_SB_METADATA_TARGET(sb.sb,
|
||||
parse_target(&sb, devs, nr_devs, fs_opt_strs.metadata_target));
|
||||
parse_target(&sb, devs, fs_opt_strs.metadata_target));
|
||||
|
||||
/* Crypt: */
|
||||
if (opts.encrypted) {
|
||||
@ -286,10 +291,10 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs,
|
||||
|
||||
bch2_sb_members_cpy_v2_v1(&sb);
|
||||
|
||||
for (i = devs; i < devs + nr_devs; i++) {
|
||||
u64 size_sectors = i->opts.fs_size >> 9;
|
||||
darray_for_each(devs, i) {
|
||||
u64 size_sectors = i->fs_size >> 9;
|
||||
|
||||
sb.sb->dev_idx = i - devs;
|
||||
sb.sb->dev_idx = i - devs.data;
|
||||
|
||||
if (!i->sb_offset) {
|
||||
i->sb_offset = BCH_SB_SECTOR;
|
||||
|
@ -45,14 +45,18 @@ struct format_opts {
|
||||
char *source;
|
||||
};
|
||||
|
||||
static inline unsigned bcachefs_kernel_version(void)
|
||||
{
|
||||
return !access("/sys/module/bcachefs/parameters/version", R_OK)
|
||||
? read_file_u64(AT_FDCWD, "/sys/module/bcachefs/parameters/version")
|
||||
: 0;
|
||||
}
|
||||
|
||||
static inline struct format_opts format_opts_default()
|
||||
{
|
||||
unsigned version = !access( "/sys/module/bcachefs/parameters/version", R_OK)
|
||||
? read_file_u64(AT_FDCWD, "/sys/module/bcachefs/parameters/version")
|
||||
: bcachefs_metadata_version_current;
|
||||
|
||||
return (struct format_opts) {
|
||||
.version = version,
|
||||
.version = bcachefs_kernel_version() ?:
|
||||
bcachefs_metadata_version_current,
|
||||
.superblock_size = SUPERBLOCK_SIZE_DEFAULT,
|
||||
};
|
||||
}
|
||||
@ -66,12 +70,15 @@ struct dev_opts {
|
||||
u64 sb_end;
|
||||
|
||||
u64 nbuckets;
|
||||
u64 fs_size;
|
||||
|
||||
const char *label; /* make this a bch_opt */
|
||||
|
||||
struct bch_opts opts;
|
||||
};
|
||||
|
||||
typedef DARRAY(struct dev_opts) dev_opts_list;
|
||||
|
||||
static inline struct dev_opts dev_opts_default()
|
||||
{
|
||||
return (struct dev_opts) { .opts = bch2_opts_empty() };
|
||||
@ -80,12 +87,13 @@ static inline struct dev_opts dev_opts_default()
|
||||
void bch2_sb_layout_init(struct bch_sb_layout *,
|
||||
unsigned, unsigned, u64, u64);
|
||||
|
||||
u64 bch2_pick_bucket_size(struct bch_opts, struct dev_opts *);
|
||||
u64 bch2_pick_bucket_size(struct bch_opts, dev_opts_list);
|
||||
void bch2_check_bucket_size(struct bch_opts, struct dev_opts *);
|
||||
|
||||
struct bch_sb *bch2_format(struct bch_opt_strs,
|
||||
struct bch_opts,
|
||||
struct format_opts, struct dev_opts *, size_t);
|
||||
struct format_opts,
|
||||
dev_opts_list devs);
|
||||
|
||||
void bch2_super_write(int, struct bch_sb *);
|
||||
struct bch_sb *__bch2_super_read(int, u64);
|
||||
|
@ -6,8 +6,8 @@
|
||||
#include "posix_to_bcachefs.h"
|
||||
#include "libbcachefs/alloc_foreground.h"
|
||||
#include "libbcachefs/buckets.h"
|
||||
#include "libbcachefs/fs-common.h"
|
||||
#include "libbcachefs/io_write.h"
|
||||
#include "libbcachefs/namei.h"
|
||||
#include "libbcachefs/str_hash.h"
|
||||
#include "libbcachefs/xattr.h"
|
||||
|
||||
@ -264,7 +264,8 @@ void copy_link(struct bch_fs *c, struct bch_inode_unpacked *dst,
|
||||
|
||||
static void copy_file(struct bch_fs *c, struct bch_inode_unpacked *dst,
|
||||
int src_fd, u64 src_size,
|
||||
char *src_path, struct copy_fs_state *s)
|
||||
char *src_path, struct copy_fs_state *s,
|
||||
u64 reserve_start)
|
||||
{
|
||||
struct fiemap_iter iter;
|
||||
struct fiemap_extent e;
|
||||
@ -295,11 +296,8 @@ static void copy_file(struct bch_fs *c, struct bch_inode_unpacked *dst,
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the data is below 1 MB, copy it so it doesn't conflict
|
||||
* with bcachefs's potentially larger superblock:
|
||||
*/
|
||||
if (e.fe_physical < 1 << 20) {
|
||||
/* If the data is in bcachefs's superblock region, copy it: */
|
||||
if (e.fe_physical < reserve_start) {
|
||||
copy_data(c, dst, src_fd, e.fe_logical,
|
||||
e.fe_logical + min(src_size - e.fe_logical,
|
||||
e.fe_length));
|
||||
@ -318,7 +316,8 @@ static void copy_file(struct bch_fs *c, struct bch_inode_unpacked *dst,
|
||||
static void copy_dir(struct copy_fs_state *s,
|
||||
struct bch_fs *c,
|
||||
struct bch_inode_unpacked *dst,
|
||||
int src_fd, const char *src_path)
|
||||
int src_fd, const char *src_path,
|
||||
u64 reserve_start)
|
||||
{
|
||||
DIR *dir = fdopendir(src_fd);
|
||||
struct dirent *d;
|
||||
@ -369,7 +368,7 @@ static void copy_dir(struct copy_fs_state *s,
|
||||
switch (mode_to_type(stat.st_mode)) {
|
||||
case DT_DIR:
|
||||
fd = xopen(d->d_name, O_RDONLY|O_NOATIME);
|
||||
copy_dir(s, c, &inode, fd, child_path);
|
||||
copy_dir(s, c, &inode, fd, child_path, reserve_start);
|
||||
close(fd);
|
||||
break;
|
||||
case DT_REG:
|
||||
@ -377,7 +376,7 @@ static void copy_dir(struct copy_fs_state *s,
|
||||
|
||||
fd = xopen(d->d_name, O_RDONLY|O_NOATIME);
|
||||
copy_file(c, &inode, fd, stat.st_size,
|
||||
child_path, s);
|
||||
child_path, s, reserve_start);
|
||||
close(fd);
|
||||
break;
|
||||
case DT_LNK:
|
||||
@ -409,7 +408,8 @@ next:
|
||||
|
||||
static void reserve_old_fs_space(struct bch_fs *c,
|
||||
struct bch_inode_unpacked *root_inode,
|
||||
ranges *extents)
|
||||
ranges *extents,
|
||||
u64 reserve_start)
|
||||
{
|
||||
struct bch_dev *ca = c->devs[0];
|
||||
struct bch_inode_unpacked dst;
|
||||
@ -422,14 +422,20 @@ static void reserve_old_fs_space(struct bch_fs *c,
|
||||
|
||||
ranges_sort_merge(extents);
|
||||
|
||||
for_each_hole(iter, *extents, bucket_to_sector(ca, ca->mi.nbuckets) << 9, i)
|
||||
link_data(c, &dst, i.start, i.start, i.end - i.start);
|
||||
for_each_hole(iter, *extents, bucket_to_sector(ca, ca->mi.nbuckets) << 9, i) {
|
||||
if (i.end <= reserve_start)
|
||||
continue;
|
||||
|
||||
u64 start = max(i.start, reserve_start);
|
||||
|
||||
link_data(c, &dst, start, start, i.end - start);
|
||||
}
|
||||
|
||||
update_inode(c, &dst);
|
||||
}
|
||||
|
||||
void copy_fs(struct bch_fs *c, int src_fd, const char *src_path,
|
||||
struct copy_fs_state *s)
|
||||
struct copy_fs_state *s, u64 reserve_start)
|
||||
{
|
||||
syncfs(src_fd);
|
||||
|
||||
@ -448,10 +454,10 @@ void copy_fs(struct bch_fs *c, int src_fd, const char *src_path,
|
||||
|
||||
|
||||
/* now, copy: */
|
||||
copy_dir(s, c, &root_inode, src_fd, src_path);
|
||||
copy_dir(s, c, &root_inode, src_fd, src_path, reserve_start);
|
||||
|
||||
if (BCH_MIGRATE_migrate == s->type)
|
||||
reserve_old_fs_space(c, &root_inode, &s->extents);
|
||||
reserve_old_fs_space(c, &root_inode, &s->extents, reserve_start);
|
||||
|
||||
update_inode(c, &root_inode);
|
||||
|
||||
|
@ -50,5 +50,5 @@ struct copy_fs_state {
|
||||
* initialized (`hardlinks` is initialized with zeroes).
|
||||
*/
|
||||
void copy_fs(struct bch_fs *c, int src_fd, const char *src_path,
|
||||
struct copy_fs_state *s);
|
||||
struct copy_fs_state *s, u64);
|
||||
#endif /* _LIBBCACHE_H */
|
||||
|
117
flake.lock
generated
117
flake.lock
generated
@ -1,17 +1,12 @@
|
||||
{
|
||||
"nodes": {
|
||||
"crane": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1721842668,
|
||||
"narHash": "sha256-k3oiD2z2AAwBFLa4+xfU+7G5fisRXfkvrMTCJrjZzXo=",
|
||||
"lastModified": 1742394900,
|
||||
"narHash": "sha256-vVOAp9ahvnU+fQoKd4SEXB2JG2wbENkpqcwlkIXgUC0=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "529c1a0b1f29f0d78fa3086b8f6a134c71ef3aaf",
|
||||
"rev": "70947c1908108c0c551ddfd73d4f750ff2ea67cd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -20,35 +15,14 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"fenix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1722320953,
|
||||
"narHash": "sha256-DfGaJtgrzcwPQYLTvjL1KaVIjpvi85b2MpM6yEGvJzM=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "483df76def3e5010d709aa3a0418ba2088503994",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -62,11 +36,11 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719994518,
|
||||
"narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=",
|
||||
"lastModified": 1741352980,
|
||||
"narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7",
|
||||
"rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -75,13 +49,33 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-github-actions": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1737420293,
|
||||
"narHash": "sha256-F1G5ifvqTpJq7fdkT34e/Jy9VCyzd5XfJ9TO8fHhJWE=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"rev": "f4158fa080ef4503c8f4c820967d946c2af31ec9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1722185531,
|
||||
"narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=",
|
||||
"lastModified": 1742422364,
|
||||
"narHash": "sha256-mNqIplmEohk5jRkqYqG19GA8MbQ/D4gQSK0Mu4LvfRQ=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d",
|
||||
"rev": "a84ebe20c6bc2ecbcfb000a50776219f48d134cc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -93,40 +87,47 @@
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1719876945,
|
||||
"narHash": "sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
|
||||
"lastModified": 1740877520,
|
||||
"narHash": "sha256-oiwv/ZK/2FhGxrCkQkB83i7GnWXPPLzoqFHpDD3uYpk=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "147dee35aab2193b174e4c0868bd80ead5ce755c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"crane": "crane",
|
||||
"fenix": "fenix",
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts",
|
||||
"nix-github-actions": "nix-github-actions",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1722262053,
|
||||
"narHash": "sha256-KxjkPVn9rQqYam6DhiN/V2NcMXtYW25maxkJoiVMpmE=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "a021b85be57d34b1eed687fcafd5d5ec64b2d853",
|
||||
"lastModified": 1742524367,
|
||||
"narHash": "sha256-KzTwk/5ETJavJZYV1DEWdCx05M4duFCxCpRbQSKWpng=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "70bf752d176b2ce07417e346d85486acea9040ef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "rust-lang",
|
||||
"ref": "nightly",
|
||||
"repo": "rust-analyzer",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@ -137,11 +138,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1722330636,
|
||||
"narHash": "sha256-uru7JzOa33YlSRwf9sfXpJG+UAV+bnBEYMjrzKrQZFw=",
|
||||
"lastModified": 1742370146,
|
||||
"narHash": "sha256-XRE8hL4vKIQyVMDXykFh4ceo3KSpuJF3ts8GKwh5bIU=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "768acdb06968e53aa1ee8de207fd955335c754b7",
|
||||
"rev": "adc195eef5da3606891cedf80c0d9ce2d3190808",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
329
flake.nix
329
flake.nix
@ -11,13 +11,10 @@
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
crane = {
|
||||
url = "github:ipetkov/crane";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
crane.url = "github:ipetkov/crane";
|
||||
|
||||
fenix = {
|
||||
url = "github:nix-community/fenix";
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
@ -25,6 +22,11 @@
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
nix-github-actions = {
|
||||
url = "github:nix-community/nix-github-actions";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs =
|
||||
@ -33,27 +35,31 @@
|
||||
nixpkgs,
|
||||
flake-parts,
|
||||
treefmt-nix,
|
||||
fenix,
|
||||
crane,
|
||||
...
|
||||
rust-overlay,
|
||||
flake-compat,
|
||||
nix-github-actions,
|
||||
}:
|
||||
let
|
||||
systems = nixpkgs.lib.filter (s: nixpkgs.lib.hasSuffix "-linux" s) nixpkgs.lib.systems.flakeExposed;
|
||||
in
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
imports = [ inputs.treefmt-nix.flakeModule ];
|
||||
|
||||
# can be extended, but these have proper binary cache support in nixpkgs
|
||||
# as of writing.
|
||||
systems = [
|
||||
"aarch64-linux"
|
||||
"x86_64-linux"
|
||||
"i686-linux"
|
||||
];
|
||||
flake = {
|
||||
githubActions = nix-github-actions.lib.mkGithubMatrix {
|
||||
# github actions supports fewer architectures
|
||||
checks = nixpkgs.lib.getAttrs [ "aarch64-linux" "x86_64-linux" ] self.checks;
|
||||
};
|
||||
};
|
||||
|
||||
inherit systems;
|
||||
|
||||
perSystem =
|
||||
{
|
||||
self',
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
system,
|
||||
...
|
||||
}:
|
||||
@ -62,119 +68,225 @@
|
||||
inherit (lib.lists) findFirst;
|
||||
inherit (lib.strings) hasPrefix removePrefix substring;
|
||||
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ (import rust-overlay) ];
|
||||
};
|
||||
|
||||
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
||||
rustfmtToml = builtins.fromTOML (builtins.readFile ./rustfmt.toml);
|
||||
|
||||
craneLib = crane.mkLib pkgs;
|
||||
|
||||
rev = self.shortRev or self.dirtyShortRev or (substring 0 8 self.lastModifiedDate);
|
||||
makefileVersion = removePrefix "VERSION=" (
|
||||
findFirst (line: hasPrefix "VERSION=" line) "VERSION=0.0.0" (split "\n" (readFile ./Makefile))
|
||||
);
|
||||
version = "${makefileVersion}+${rev}";
|
||||
|
||||
commonArgs = {
|
||||
inherit version;
|
||||
src = self;
|
||||
mkCommon =
|
||||
{
|
||||
crane,
|
||||
pkgs,
|
||||
rustVersion ? "latest",
|
||||
|
||||
env = {
|
||||
PKG_CONFIG_SYSTEMD_SYSTEMDSYSTEMUNITDIR = "${placeholder "out"}/lib/systemd/system";
|
||||
PKG_CONFIG_UDEV_UDEVDIR = "${placeholder "out"}/lib/udev";
|
||||
# build time
|
||||
buildPackages,
|
||||
pkg-config,
|
||||
rustPlatform,
|
||||
stdenv,
|
||||
|
||||
# run time
|
||||
keyutils,
|
||||
libaio,
|
||||
libsodium,
|
||||
liburcu,
|
||||
libuuid,
|
||||
lz4,
|
||||
udev,
|
||||
zlib,
|
||||
zstd,
|
||||
}:
|
||||
let
|
||||
inherit (stdenv) cc hostPlatform;
|
||||
|
||||
craneLib = (crane.mkLib pkgs).overrideToolchain (
|
||||
p: p.rust-bin.stable."${rustVersion}".minimal.override { extensions = [ "clippy" ]; }
|
||||
);
|
||||
|
||||
args = {
|
||||
inherit version;
|
||||
src = self;
|
||||
strictDeps = true;
|
||||
|
||||
env = {
|
||||
PKG_CONFIG_SYSTEMD_SYSTEMDSYSTEMUNITDIR = "${placeholder "out"}/lib/systemd/system";
|
||||
PKG_CONFIG_UDEV_UDEVDIR = "${placeholder "out"}/lib/udev";
|
||||
|
||||
CARGO_BUILD_TARGET = hostPlatform.rust.rustcTargetSpec;
|
||||
"CARGO_TARGET_${hostPlatform.rust.cargoEnvVarTarget}_LINKER" = "${cc.targetPrefix}cc";
|
||||
HOST_CC = "${cc.nativePrefix}cc";
|
||||
TARGET_CC = "${cc.targetPrefix}cc";
|
||||
};
|
||||
|
||||
makeFlags = [
|
||||
"INITRAMFS_DIR=${placeholder "out"}/etc/initramfs-tools"
|
||||
"PREFIX=${placeholder "out"}"
|
||||
"VERSION=${version}"
|
||||
];
|
||||
|
||||
dontStrip = true;
|
||||
|
||||
depsBuildBuild = [
|
||||
buildPackages.stdenv.cc
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkg-config
|
||||
rustPlatform.bindgenHook
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
keyutils
|
||||
libaio
|
||||
libsodium
|
||||
liburcu
|
||||
libuuid
|
||||
lz4
|
||||
udev
|
||||
zlib
|
||||
zstd
|
||||
];
|
||||
|
||||
meta = {
|
||||
description = "Userspace tools for bcachefs";
|
||||
license = lib.licenses.gpl2Only;
|
||||
mainProgram = "bcachefs";
|
||||
};
|
||||
};
|
||||
|
||||
cargoArtifacts = craneLib.buildDepsOnly args;
|
||||
in
|
||||
{
|
||||
inherit args cargoArtifacts craneLib;
|
||||
};
|
||||
common = pkgs.callPackage mkCommon { inherit crane; };
|
||||
|
||||
makeFlags = [
|
||||
"INITRAMFS_DIR=${placeholder "out"}/etc/initramfs-tools"
|
||||
"PREFIX=${placeholder "out"}"
|
||||
"VERSION=${version}"
|
||||
];
|
||||
mkPackage =
|
||||
{ common, name }:
|
||||
common.craneLib.buildPackage (
|
||||
common.args
|
||||
// {
|
||||
inherit (common) cargoArtifacts;
|
||||
pname = name;
|
||||
|
||||
dontStrip = true;
|
||||
enableParallelBuilding = true;
|
||||
buildPhaseCargoCommand = ''
|
||||
make ''${enableParallelBuilding:+-j''${NIX_BUILD_CORES}} $makeFlags
|
||||
'';
|
||||
doNotPostBuildInstallCargoBinaries = true;
|
||||
installPhaseCommand = ''
|
||||
make ''${enableParallelBuilding:+-j''${NIX_BUILD_CORES}} $makeFlags install
|
||||
'';
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
pkg-config
|
||||
rustPlatform.bindgenHook
|
||||
];
|
||||
doInstallCheck = true;
|
||||
installCheckPhase = ''
|
||||
runHook preInstallCheck
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
attr
|
||||
keyutils
|
||||
libaio
|
||||
libsodium
|
||||
liburcu
|
||||
libuuid
|
||||
lz4
|
||||
udev
|
||||
zlib
|
||||
zstd
|
||||
];
|
||||
test "$($out/bin/bcachefs version)" = "${version}"
|
||||
|
||||
meta = {
|
||||
description = "Userspace tools for bcachefs";
|
||||
license = lib.licenses.gpl2Only;
|
||||
mainProgram = "bcachefs";
|
||||
};
|
||||
};
|
||||
runHook postInstallCheck
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
cargoArtifacts = craneLib.buildDepsOnly (commonArgs // { pname = cargoToml.package.name; });
|
||||
mkPackages =
|
||||
name: systems:
|
||||
let
|
||||
packagesForSystem =
|
||||
crossSystem:
|
||||
let
|
||||
localSystem = system;
|
||||
pkgs' = import nixpkgs {
|
||||
inherit crossSystem localSystem;
|
||||
overlays = [ (import rust-overlay) ];
|
||||
};
|
||||
|
||||
common = pkgs'.callPackage mkCommon { inherit crane; };
|
||||
package = pkgs'.callPackage mkPackage { inherit common name; };
|
||||
packageFuse = package.overrideAttrs (
|
||||
final: prev: {
|
||||
makeFlags = prev.makeFlags ++ [ "BCACHEFS_FUSE=1" ];
|
||||
buildInputs = prev.buildInputs ++ [ pkgs'.fuse3 ];
|
||||
}
|
||||
);
|
||||
in
|
||||
[
|
||||
(lib.nameValuePair "${name}-${crossSystem}" package)
|
||||
(lib.nameValuePair "${name}-fuse-${crossSystem}" packageFuse)
|
||||
];
|
||||
in
|
||||
lib.listToAttrs (lib.flatten (map packagesForSystem systems));
|
||||
in
|
||||
{
|
||||
packages.default = config.packages.bcachefs-tools;
|
||||
packages.bcachefs-tools = craneLib.buildPackage (
|
||||
commonArgs
|
||||
packages =
|
||||
let
|
||||
inherit (cargoToml.package) name;
|
||||
in
|
||||
(mkPackages name systems)
|
||||
// {
|
||||
inherit cargoArtifacts;
|
||||
${name} = config.packages."${name}-${system}";
|
||||
"${name}-fuse" = config.packages."${name}-fuse-${system}";
|
||||
default = config.packages.${name};
|
||||
};
|
||||
|
||||
enableParallelBuilding = true;
|
||||
buildPhaseCargoCommand = ''
|
||||
make ''${enableParallelBuilding:+-j''${NIX_BUILD_CORES}} $makeFlags
|
||||
'';
|
||||
installPhaseCommand = ''
|
||||
make ''${enableParallelBuilding:+-j''${NIX_BUILD_CORES}} $makeFlags install
|
||||
'';
|
||||
checks = {
|
||||
inherit (config.packages)
|
||||
bcachefs-tools
|
||||
bcachefs-tools-fuse
|
||||
bcachefs-tools-fuse-i686-linux
|
||||
;
|
||||
|
||||
doInstallCheck = true;
|
||||
installCheckPhase = ''
|
||||
runHook preInstallCheck
|
||||
cargo-clippy = common.craneLib.cargoClippy (
|
||||
common.args
|
||||
// {
|
||||
inherit (common) cargoArtifacts;
|
||||
cargoClippyExtraArgs = "--all-targets --all-features -- --deny warnings";
|
||||
}
|
||||
);
|
||||
|
||||
test "$($out/bin/bcachefs version)" = "${version}"
|
||||
# we have to build our own `craneLib.cargoTest`
|
||||
cargo-test = common.craneLib.mkCargoDerivation (
|
||||
common.args
|
||||
// {
|
||||
inherit (common) cargoArtifacts;
|
||||
doCheck = true;
|
||||
|
||||
runHook postInstallCheck
|
||||
'';
|
||||
}
|
||||
);
|
||||
enableParallelChecking = true;
|
||||
|
||||
packages.bcachefs-tools-fuse = config.packages.bcachefs-tools.overrideAttrs (
|
||||
final: prev: {
|
||||
makeFlags = prev.makeFlags ++ [ "BCACHEFS_FUSE=1" ];
|
||||
buildInputs = prev.buildInputs ++ [ pkgs.fuse3 ];
|
||||
}
|
||||
);
|
||||
pnameSuffix = "-test";
|
||||
buildPhaseCargoCommand = "";
|
||||
checkPhaseCargoCommand = ''
|
||||
make ''${enableParallelChecking:+-j''${NIX_BUILD_CORES}} $makeFlags libbcachefs.a
|
||||
cargo test --profile release -- --nocapture
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
checks.cargo-clippy = craneLib.cargoClippy (
|
||||
commonArgs
|
||||
// {
|
||||
inherit cargoArtifacts;
|
||||
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
|
||||
}
|
||||
);
|
||||
|
||||
# we have to build our own `craneLib.cargoTest`
|
||||
checks.cargo-test = craneLib.mkCargoDerivation (
|
||||
commonArgs
|
||||
// {
|
||||
inherit cargoArtifacts;
|
||||
doCheck = true;
|
||||
|
||||
enableParallelChecking = true;
|
||||
|
||||
pnameSuffix = "-test";
|
||||
buildPhaseCargoCommand = "";
|
||||
checkPhaseCargoCommand = ''
|
||||
make ''${enableParallelChecking:+-j''${NIX_BUILD_CORES}} $makeFlags libbcachefs.a
|
||||
cargo test --profile release -- --nocapture
|
||||
'';
|
||||
}
|
||||
);
|
||||
# cargo clippy with the current minimum supported rust version
|
||||
# according to Cargo.toml
|
||||
msrv =
|
||||
let
|
||||
rustVersion = cargoToml.package.rust-version;
|
||||
common = pkgs.callPackage mkCommon { inherit crane rustVersion; };
|
||||
in
|
||||
common.craneLib.cargoClippy (
|
||||
common.args
|
||||
// {
|
||||
pname = "msrv";
|
||||
inherit (common) cargoArtifacts;
|
||||
cargoClippyExtraArgs = "--all-targets --all-features -- --deny warnings";
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
inputsFrom = [
|
||||
@ -190,9 +302,12 @@
|
||||
cargo-audit
|
||||
cargo-outdated
|
||||
clang-tools
|
||||
clippy
|
||||
rust-analyzer
|
||||
rustc
|
||||
(rust-bin.stable.latest.minimal.override {
|
||||
extensions = [
|
||||
"rust-analyzer"
|
||||
"rust-src"
|
||||
];
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
@ -204,7 +319,7 @@
|
||||
nixfmt.enable = true;
|
||||
rustfmt.edition = rustfmtToml.edition;
|
||||
rustfmt.enable = true;
|
||||
rustfmt.package = fenix.packages.${system}.default.rustfmt;
|
||||
rustfmt.package = pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.rustfmt);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -67,6 +67,7 @@
|
||||
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
|
||||
#define fallthrough __attribute__((__fallthrough__))
|
||||
#define __noreturn __attribute__((__noreturn__))
|
||||
#define __no_kmsan_checks
|
||||
|
||||
#ifndef __counted_by
|
||||
#define __counted_by(nr)
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/byteorder.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/kmsan-checks.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/minmax.h>
|
||||
|
||||
|
98
include/linux/kmsan-checks.h
Normal file
98
include/linux/kmsan-checks.h
Normal file
@ -0,0 +1,98 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* KMSAN checks to be used for one-off annotations in subsystems.
|
||||
*
|
||||
* Copyright (C) 2017-2022 Google LLC
|
||||
* Author: Alexander Potapenko <glider@google.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_KMSAN_CHECKS_H
|
||||
#define _LINUX_KMSAN_CHECKS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef CONFIG_KMSAN
|
||||
|
||||
/**
|
||||
* kmsan_poison_memory() - Mark the memory range as uninitialized.
|
||||
* @address: address to start with.
|
||||
* @size: size of buffer to poison.
|
||||
* @flags: GFP flags for allocations done by this function.
|
||||
*
|
||||
* Until other data is written to this range, KMSAN will treat it as
|
||||
* uninitialized. Error reports for this memory will reference the call site of
|
||||
* kmsan_poison_memory() as origin.
|
||||
*/
|
||||
void kmsan_poison_memory(const void *address, size_t size, gfp_t flags);
|
||||
|
||||
/**
|
||||
* kmsan_unpoison_memory() - Mark the memory range as initialized.
|
||||
* @address: address to start with.
|
||||
* @size: size of buffer to unpoison.
|
||||
*
|
||||
* Until other data is written to this range, KMSAN will treat it as
|
||||
* initialized.
|
||||
*/
|
||||
void kmsan_unpoison_memory(const void *address, size_t size);
|
||||
|
||||
/**
|
||||
* kmsan_check_memory() - Check the memory range for being initialized.
|
||||
* @address: address to start with.
|
||||
* @size: size of buffer to check.
|
||||
*
|
||||
* If any piece of the given range is marked as uninitialized, KMSAN will report
|
||||
* an error.
|
||||
*/
|
||||
void kmsan_check_memory(const void *address, size_t size);
|
||||
|
||||
/**
|
||||
* kmsan_copy_to_user() - Notify KMSAN about a data transfer to userspace.
|
||||
* @to: destination address in the userspace.
|
||||
* @from: source address in the kernel.
|
||||
* @to_copy: number of bytes to copy.
|
||||
* @left: number of bytes not copied.
|
||||
*
|
||||
* If this is a real userspace data transfer, KMSAN checks the bytes that were
|
||||
* actually copied to ensure there was no information leak. If @to belongs to
|
||||
* the kernel space (which is possible for compat syscalls), KMSAN just copies
|
||||
* the metadata.
|
||||
*/
|
||||
void kmsan_copy_to_user(void __user *to, const void *from, size_t to_copy,
|
||||
size_t left);
|
||||
|
||||
/**
|
||||
* kmsan_memmove() - Notify KMSAN about a data copy within kernel.
|
||||
* @to: destination address in the kernel.
|
||||
* @from: source address in the kernel.
|
||||
* @size: number of bytes to copy.
|
||||
*
|
||||
* Invoked after non-instrumented version (e.g. implemented using assembly
|
||||
* code) of memmove()/memcpy() is called, in order to copy KMSAN's metadata.
|
||||
*/
|
||||
void kmsan_memmove(void *to, const void *from, size_t to_copy);
|
||||
|
||||
#else
|
||||
|
||||
static inline void kmsan_poison_memory(const void *address, size_t size,
|
||||
gfp_t flags)
|
||||
{
|
||||
}
|
||||
static inline void kmsan_unpoison_memory(const void *address, size_t size)
|
||||
{
|
||||
}
|
||||
static inline void kmsan_check_memory(const void *address, size_t size)
|
||||
{
|
||||
}
|
||||
static inline void kmsan_copy_to_user(void __user *to, const void *from,
|
||||
size_t to_copy, size_t left)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void kmsan_memmove(void *to, const void *from, size_t to_copy)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_KMSAN_CHECKS_H */
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct sysinfo {
|
||||
|
6
include/linux/sched/sysctl.h
Normal file
6
include/linux/sched/sysctl.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef __TOOLS_LINUX_SCHED_SYSCTL_H
|
||||
#define __TOOLS_LINUX_SCHED_SYSCTL_H
|
||||
|
||||
#define sysctl_hung_task_timeout_secs (HZ * 10)
|
||||
|
||||
#endif /* __TOOLS_LINUX_SCHED_SYSCTL_H */
|
@ -589,6 +589,8 @@ iter_err:
|
||||
|
||||
int bch2_alloc_read(struct bch_fs *c)
|
||||
{
|
||||
down_read(&c->state_lock);
|
||||
|
||||
struct btree_trans *trans = bch2_trans_get(c);
|
||||
struct bch_dev *ca = NULL;
|
||||
int ret;
|
||||
@ -652,6 +654,7 @@ int bch2_alloc_read(struct bch_fs *c)
|
||||
bch2_dev_put(ca);
|
||||
bch2_trans_put(trans);
|
||||
|
||||
up_read(&c->state_lock);
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
}
|
||||
@ -673,8 +676,7 @@ static int __need_discard_or_freespace_err(struct btree_trans *trans,
|
||||
bch2_bkey_val_to_text(&buf, c, alloc_k);
|
||||
|
||||
int ret = __bch2_fsck_err(NULL, trans, flags, err_id,
|
||||
"bucket incorrectly %sset in %s btree\n"
|
||||
" %s",
|
||||
"bucket incorrectly %sset in %s btree\n%s",
|
||||
set ? "" : "un",
|
||||
bch2_btree_id_str(btree),
|
||||
buf.buf);
|
||||
@ -777,14 +779,12 @@ static inline int bch2_dev_data_type_accounting_mod(struct btree_trans *trans, s
|
||||
s64 delta_sectors,
|
||||
s64 delta_fragmented, unsigned flags)
|
||||
{
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_dev_data_type,
|
||||
.dev_data_type.dev = ca->dev_idx,
|
||||
.dev_data_type.data_type = data_type,
|
||||
};
|
||||
s64 d[3] = { delta_buckets, delta_sectors, delta_fragmented };
|
||||
|
||||
return bch2_disk_accounting_mod(trans, &acc, d, 3, flags & BTREE_TRIGGER_gc);
|
||||
return bch2_disk_accounting_mod2(trans, flags & BTREE_TRIGGER_gc,
|
||||
d, dev_data_type,
|
||||
.dev = ca->dev_idx,
|
||||
.data_type = data_type);
|
||||
}
|
||||
|
||||
int bch2_alloc_key_to_dev_counters(struct btree_trans *trans, struct bch_dev *ca,
|
||||
@ -837,7 +837,7 @@ int bch2_trigger_alloc(struct btree_trans *trans,
|
||||
|
||||
struct bch_dev *ca = bch2_dev_bucket_tryget(c, new.k->p);
|
||||
if (!ca)
|
||||
return -EIO;
|
||||
return -BCH_ERR_trigger_alloc;
|
||||
|
||||
struct bch_alloc_v4 old_a_convert;
|
||||
const struct bch_alloc_v4 *old_a = bch2_alloc_to_v4(old, &old_a_convert);
|
||||
@ -1029,9 +1029,9 @@ fsck_err:
|
||||
bch2_dev_put(ca);
|
||||
return ret;
|
||||
invalid_bucket:
|
||||
bch2_fs_inconsistent(c, "reference to invalid bucket\n %s",
|
||||
bch2_fs_inconsistent(c, "reference to invalid bucket\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, new.s_c), buf.buf));
|
||||
ret = -EIO;
|
||||
ret = -BCH_ERR_trigger_alloc;
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -1203,8 +1203,7 @@ int bch2_check_alloc_key(struct btree_trans *trans,
|
||||
|
||||
if (fsck_err_on(a->gen != alloc_gen(k, gens_offset),
|
||||
trans, bucket_gens_key_wrong,
|
||||
"incorrect gen in bucket_gens btree (got %u should be %u)\n"
|
||||
" %s",
|
||||
"incorrect gen in bucket_gens btree (got %u should be %u)\n%s",
|
||||
alloc_gen(k, gens_offset), a->gen,
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, alloc_k), buf.buf))) {
|
||||
@ -1262,7 +1261,7 @@ int bch2_check_alloc_hole_freespace(struct btree_trans *trans,
|
||||
if (fsck_err_on(k.k->type != KEY_TYPE_set,
|
||||
trans, freespace_hole_missing,
|
||||
"hole in alloc btree missing in freespace btree\n"
|
||||
" device %llu buckets %llu-%llu",
|
||||
"device %llu buckets %llu-%llu",
|
||||
freespace_iter->pos.inode,
|
||||
freespace_iter->pos.offset,
|
||||
end->offset)) {
|
||||
@ -1421,7 +1420,7 @@ int bch2_check_discard_freespace_key(struct btree_trans *trans, struct btree_ite
|
||||
(state == BCH_DATA_free &&
|
||||
genbits != alloc_freespace_genbits(*a))) {
|
||||
if (fsck_err(trans, need_discard_freespace_key_bad,
|
||||
"%s\n incorrectly set at %s:%llu:%llu:0 (free %u, genbits %llu should be %llu)",
|
||||
"%s\nincorrectly set at %s:%llu:%llu:0 (free %u, genbits %llu should be %llu)",
|
||||
(bch2_bkey_val_to_text(&buf, c, alloc_k), buf.buf),
|
||||
bch2_btree_id_str(iter->btree_id),
|
||||
iter->pos.inode,
|
||||
@ -1502,7 +1501,7 @@ int bch2_check_bucket_gens_key(struct btree_trans *trans,
|
||||
struct bch_dev *ca = bch2_dev_tryget_noerror(c, k.k->p.inode);
|
||||
if (!ca) {
|
||||
if (fsck_err(trans, bucket_gens_to_invalid_dev,
|
||||
"bucket_gens key for invalid device:\n %s",
|
||||
"bucket_gens key for invalid device:\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
||||
ret = bch2_btree_delete_at(trans, iter, 0);
|
||||
goto out;
|
||||
@ -1511,7 +1510,7 @@ int bch2_check_bucket_gens_key(struct btree_trans *trans,
|
||||
if (fsck_err_on(end <= ca->mi.first_bucket ||
|
||||
start >= ca->mi.nbuckets,
|
||||
trans, bucket_gens_to_invalid_buckets,
|
||||
"bucket_gens key for invalid buckets:\n %s",
|
||||
"bucket_gens key for invalid buckets:\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
ret = bch2_btree_delete_at(trans, iter, 0);
|
||||
goto out;
|
||||
@ -1714,8 +1713,7 @@ static int bch2_check_alloc_to_lru_ref(struct btree_trans *trans,
|
||||
|
||||
if (fsck_err_on(!a->io_time[READ],
|
||||
trans, alloc_key_cached_but_read_time_zero,
|
||||
"cached bucket with read_time 0\n"
|
||||
" %s",
|
||||
"cached bucket with read_time 0\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, alloc_k), buf.buf))) {
|
||||
struct bkey_i_alloc_v4 *a_mut =
|
||||
|
@ -127,14 +127,14 @@ void __bch2_open_bucket_put(struct bch_fs *c, struct open_bucket *ob)
|
||||
|
||||
void bch2_open_bucket_write_error(struct bch_fs *c,
|
||||
struct open_buckets *obs,
|
||||
unsigned dev)
|
||||
unsigned dev, int err)
|
||||
{
|
||||
struct open_bucket *ob;
|
||||
unsigned i;
|
||||
|
||||
open_bucket_for_each(c, obs, ob, i)
|
||||
if (ob->dev == dev && ob->ec)
|
||||
bch2_ec_bucket_cancel(c, ob);
|
||||
bch2_ec_bucket_cancel(c, ob, err);
|
||||
}
|
||||
|
||||
static struct open_bucket *bch2_open_bucket_alloc(struct bch_fs *c)
|
||||
@ -631,7 +631,7 @@ static inline void bch2_dev_stripe_increment_inlined(struct bch_dev *ca,
|
||||
struct bch_dev_usage *usage)
|
||||
{
|
||||
u64 *v = stripe->next_alloc + ca->dev_idx;
|
||||
u64 free_space = dev_buckets_available(ca, BCH_WATERMARK_normal);
|
||||
u64 free_space = __dev_buckets_available(ca, *usage, BCH_WATERMARK_normal);
|
||||
u64 free_space_inv = free_space
|
||||
? div64_u64(1ULL << 48, free_space)
|
||||
: 1ULL << 48;
|
||||
|
@ -82,7 +82,7 @@ static inline struct open_bucket *ec_open_bucket(struct bch_fs *c,
|
||||
}
|
||||
|
||||
void bch2_open_bucket_write_error(struct bch_fs *,
|
||||
struct open_buckets *, unsigned);
|
||||
struct open_buckets *, unsigned, int);
|
||||
|
||||
void __bch2_open_bucket_put(struct bch_fs *, struct open_bucket *);
|
||||
|
||||
|
@ -50,6 +50,8 @@ void bch2_backpointer_to_text(struct printbuf *out, struct bch_fs *c, struct bke
|
||||
}
|
||||
|
||||
bch2_btree_id_level_to_text(out, bp.v->btree_id, bp.v->level);
|
||||
prt_str(out, " data_type=");
|
||||
bch2_prt_data_type(out, bp.v->data_type);
|
||||
prt_printf(out, " suboffset=%u len=%u gen=%u pos=",
|
||||
(u32) bp.k->p.offset & ~(~0U << MAX_EXTENT_COMPRESS_RATIO_SHIFT),
|
||||
bp.v->bucket_len,
|
||||
@ -94,6 +96,7 @@ static noinline int backpointer_mod_err(struct btree_trans *trans,
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
int ret = 0;
|
||||
|
||||
if (insert) {
|
||||
prt_printf(&buf, "existing backpointer found when inserting ");
|
||||
@ -123,17 +126,15 @@ static noinline int backpointer_mod_err(struct btree_trans *trans,
|
||||
|
||||
prt_printf(&buf, "for ");
|
||||
bch2_bkey_val_to_text(&buf, c, orig_k);
|
||||
|
||||
bch_err(c, "%s", buf.buf);
|
||||
}
|
||||
|
||||
if (c->curr_recovery_pass > BCH_RECOVERY_PASS_check_extents_to_backpointers &&
|
||||
__bch2_inconsistent_error(c, &buf))
|
||||
ret = -BCH_ERR_erofs_unfixed_errors;
|
||||
|
||||
bch_err(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
|
||||
if (c->curr_recovery_pass > BCH_RECOVERY_PASS_check_extents_to_backpointers) {
|
||||
return bch2_inconsistent_error(c) ? BCH_ERR_erofs_unfixed_errors : 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bch2_bucket_backpointer_mod_nowritebuffer(struct btree_trans *trans,
|
||||
@ -208,11 +209,11 @@ static int backpointer_target_not_found(struct btree_trans *trans,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
prt_printf(&buf, "backpointer doesn't match %s it points to:\n ",
|
||||
prt_printf(&buf, "backpointer doesn't match %s it points to:\n",
|
||||
bp.v->level ? "btree node" : "extent");
|
||||
bch2_bkey_val_to_text(&buf, c, bp.s_c);
|
||||
|
||||
prt_printf(&buf, "\n ");
|
||||
prt_newline(&buf);
|
||||
bch2_bkey_val_to_text(&buf, c, target_k);
|
||||
|
||||
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(target_k);
|
||||
@ -220,7 +221,7 @@ static int backpointer_target_not_found(struct btree_trans *trans,
|
||||
struct extent_ptr_decoded p;
|
||||
bkey_for_each_ptr_decode(target_k.k, ptrs, p, entry)
|
||||
if (p.ptr.dev == bp.k->p.inode) {
|
||||
prt_printf(&buf, "\n ");
|
||||
prt_newline(&buf);
|
||||
struct bkey_i_backpointer bp2;
|
||||
bch2_extent_ptr_to_bp(c, bp.v->btree_id, bp.v->level, target_k, p, entry, &bp2);
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&bp2.k_i));
|
||||
@ -441,12 +442,11 @@ found:
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
prt_str(&buf, "extents pointing to same space, but first extent checksum bad:");
|
||||
prt_printf(&buf, "\n ");
|
||||
prt_printf(&buf, "extents pointing to same space, but first extent checksum bad:\n");
|
||||
bch2_btree_id_to_text(&buf, btree);
|
||||
prt_str(&buf, " ");
|
||||
bch2_bkey_val_to_text(&buf, c, extent);
|
||||
prt_printf(&buf, "\n ");
|
||||
prt_newline(&buf);
|
||||
bch2_btree_id_to_text(&buf, o_btree);
|
||||
prt_str(&buf, " ");
|
||||
bch2_bkey_val_to_text(&buf, c, extent2);
|
||||
@ -537,9 +537,9 @@ check_existing_bp:
|
||||
|
||||
if (bch2_extents_match(orig_k, other_extent)) {
|
||||
printbuf_reset(&buf);
|
||||
prt_printf(&buf, "duplicate versions of same extent, deleting smaller\n ");
|
||||
prt_printf(&buf, "duplicate versions of same extent, deleting smaller\n");
|
||||
bch2_bkey_val_to_text(&buf, c, orig_k);
|
||||
prt_str(&buf, "\n ");
|
||||
prt_newline(&buf);
|
||||
bch2_bkey_val_to_text(&buf, c, other_extent);
|
||||
bch_err(c, "%s", buf.buf);
|
||||
|
||||
@ -578,20 +578,20 @@ check_existing_bp:
|
||||
}
|
||||
|
||||
printbuf_reset(&buf);
|
||||
prt_printf(&buf, "duplicate extents pointing to same space on dev %llu\n ", bp->k.p.inode);
|
||||
prt_printf(&buf, "duplicate extents pointing to same space on dev %llu\n", bp->k.p.inode);
|
||||
bch2_bkey_val_to_text(&buf, c, orig_k);
|
||||
prt_str(&buf, "\n ");
|
||||
prt_newline(&buf);
|
||||
bch2_bkey_val_to_text(&buf, c, other_extent);
|
||||
bch_err(c, "%s", buf.buf);
|
||||
ret = -BCH_ERR_fsck_repair_unimplemented;
|
||||
goto err;
|
||||
missing:
|
||||
printbuf_reset(&buf);
|
||||
prt_str(&buf, "missing backpointer\n for: ");
|
||||
prt_str(&buf, "missing backpointer\nfor: ");
|
||||
bch2_bkey_val_to_text(&buf, c, orig_k);
|
||||
prt_printf(&buf, "\n want: ");
|
||||
prt_printf(&buf, "\nwant: ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&bp->k_i));
|
||||
prt_printf(&buf, "\n got: ");
|
||||
prt_printf(&buf, "\ngot: ");
|
||||
bch2_bkey_val_to_text(&buf, c, bp_k);
|
||||
|
||||
if (fsck_err(trans, ptr_to_missing_backpointer, "%s", buf.buf))
|
||||
@ -782,7 +782,7 @@ enum alloc_sector_counter {
|
||||
ALLOC_SECTORS_NR
|
||||
};
|
||||
|
||||
static enum alloc_sector_counter data_type_to_alloc_counter(enum bch_data_type t)
|
||||
static int data_type_to_alloc_counter(enum bch_data_type t)
|
||||
{
|
||||
switch (t) {
|
||||
case BCH_DATA_btree:
|
||||
@ -791,9 +791,10 @@ static enum alloc_sector_counter data_type_to_alloc_counter(enum bch_data_type t
|
||||
case BCH_DATA_cached:
|
||||
return ALLOC_cached;
|
||||
case BCH_DATA_stripe:
|
||||
case BCH_DATA_parity:
|
||||
return ALLOC_stripe;
|
||||
default:
|
||||
BUG();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -844,7 +845,11 @@ static int check_bucket_backpointer_mismatch(struct btree_trans *trans, struct b
|
||||
if (bp.v->bucket_gen != a->gen)
|
||||
continue;
|
||||
|
||||
sectors[data_type_to_alloc_counter(bp.v->data_type)] += bp.v->bucket_len;
|
||||
int alloc_counter = data_type_to_alloc_counter(bp.v->data_type);
|
||||
if (alloc_counter < 0)
|
||||
continue;
|
||||
|
||||
sectors[alloc_counter] += bp.v->bucket_len;
|
||||
};
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
if (ret)
|
||||
@ -1016,7 +1021,7 @@ int bch2_check_extents_to_backpointers(struct bch_fs *c)
|
||||
* Can't allow devices to come/go/resize while we have bucket bitmaps
|
||||
* allocated
|
||||
*/
|
||||
lockdep_assert_held(&c->state_lock);
|
||||
down_read(&c->state_lock);
|
||||
|
||||
for_each_member_device(c, ca) {
|
||||
BUG_ON(ca->bucket_backpointer_mismatches);
|
||||
@ -1101,6 +1106,7 @@ err_free_bitmaps:
|
||||
ca->bucket_backpointer_mismatches = NULL;
|
||||
}
|
||||
|
||||
up_read(&c->state_lock);
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1143,7 +1143,8 @@ static inline __u64 __bset_magic(struct bch_sb *sb)
|
||||
x(log, 9) \
|
||||
x(overwrite, 10) \
|
||||
x(write_buffer_keys, 11) \
|
||||
x(datetime, 12)
|
||||
x(datetime, 12) \
|
||||
x(log_bkey, 13)
|
||||
|
||||
enum bch_jset_entry_type {
|
||||
#define x(f, nr) BCH_JSET_ENTRY_##f = nr,
|
||||
|
@ -1417,7 +1417,7 @@ void __bch2_btree_pos_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
prt_printf(out, "%u", r->level);
|
||||
else
|
||||
prt_printf(out, "(unknown)");
|
||||
prt_printf(out, "\n ");
|
||||
prt_newline(out);
|
||||
|
||||
bch2_bkey_val_to_text(out, c, k);
|
||||
}
|
||||
|
@ -213,15 +213,15 @@ static int btree_check_node_boundaries(struct btree_trans *trans, struct btree *
|
||||
|
||||
prt_printf(&buf, " at ");
|
||||
bch2_btree_id_level_to_text(&buf, b->c.btree_id, b->c.level);
|
||||
prt_printf(&buf, ":\n parent: ");
|
||||
prt_printf(&buf, ":\nparent: ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
|
||||
|
||||
if (prev) {
|
||||
prt_printf(&buf, "\n prev: ");
|
||||
prt_printf(&buf, "\nprev: ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&prev->key));
|
||||
}
|
||||
|
||||
prt_str(&buf, "\n next: ");
|
||||
prt_str(&buf, "\nnext: ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&cur->key));
|
||||
|
||||
if (bpos_lt(expected_start, cur->data->min_key)) { /* gap */
|
||||
@ -280,12 +280,12 @@ static int btree_repair_node_end(struct btree_trans *trans, struct btree *b,
|
||||
if (bpos_eq(child->key.k.p, b->key.k.p))
|
||||
return 0;
|
||||
|
||||
prt_printf(&buf, " at ");
|
||||
prt_printf(&buf, "\nat: ");
|
||||
bch2_btree_id_level_to_text(&buf, b->c.btree_id, b->c.level);
|
||||
prt_printf(&buf, ":\n parent: ");
|
||||
prt_printf(&buf, "\nparent: ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
|
||||
|
||||
prt_str(&buf, "\n child: ");
|
||||
prt_str(&buf, "\nchild: ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&child->key));
|
||||
|
||||
if (mustfix_fsck_err(trans, btree_node_topology_bad_max_key,
|
||||
@ -351,8 +351,7 @@ again:
|
||||
|
||||
if (mustfix_fsck_err_on(bch2_err_matches(ret, EIO),
|
||||
trans, btree_node_read_error,
|
||||
"Topology repair: unreadable btree node at\n"
|
||||
" %s",
|
||||
"Topology repair: unreadable btree node at\n%s",
|
||||
buf.buf)) {
|
||||
bch2_btree_node_evict(trans, cur_k.k);
|
||||
cur = NULL;
|
||||
@ -612,7 +611,7 @@ static int bch2_gc_mark_key(struct btree_trans *trans, enum btree_id btree_id,
|
||||
if (fsck_err_on(btree_id != BTREE_ID_accounting &&
|
||||
k.k->bversion.lo > atomic64_read(&c->key_version),
|
||||
trans, bkey_version_in_future,
|
||||
"key version number higher than recorded %llu\n %s",
|
||||
"key version number higher than recorded %llu\n%s",
|
||||
atomic64_read(&c->key_version),
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
||||
atomic64_set(&c->key_version, k.k->bversion.lo);
|
||||
@ -620,7 +619,7 @@ static int bch2_gc_mark_key(struct btree_trans *trans, enum btree_id btree_id,
|
||||
|
||||
if (mustfix_fsck_err_on(level && !bch2_dev_btree_bitmap_marked(c, k),
|
||||
trans, btree_bitmap_not_marked,
|
||||
"btree ptr not marked in member info btree allocated bitmap\n %s",
|
||||
"btree ptr not marked in member info btree allocated bitmap\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k),
|
||||
buf.buf))) {
|
||||
@ -1021,8 +1020,7 @@ int bch2_check_allocations(struct bch_fs *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&c->state_lock);
|
||||
|
||||
down_read(&c->state_lock);
|
||||
down_write(&c->gc_lock);
|
||||
|
||||
bch2_btree_interior_updates_flush(c);
|
||||
@ -1060,6 +1058,7 @@ out:
|
||||
percpu_up_write(&c->mark_lock);
|
||||
|
||||
up_write(&c->gc_lock);
|
||||
up_read(&c->state_lock);
|
||||
|
||||
/*
|
||||
* At startup, allocations can happen directly instead of via the
|
||||
|
@ -525,8 +525,6 @@ static void btree_err_msg(struct printbuf *out, struct bch_fs *c,
|
||||
prt_printf(out, "at btree ");
|
||||
bch2_btree_pos_to_text(out, c, b);
|
||||
|
||||
printbuf_indent_add(out, 2);
|
||||
|
||||
prt_printf(out, "\nnode offset %u/%u",
|
||||
b->written, btree_ptr_sectors_written(bkey_i_to_s_c(&b->key)));
|
||||
if (i)
|
||||
@ -550,23 +548,7 @@ static int __btree_err(int ret,
|
||||
enum bch_sb_error_id err_type,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
struct printbuf out = PRINTBUF;
|
||||
bool silent = c->curr_recovery_pass == BCH_RECOVERY_PASS_scan_for_btree_nodes;
|
||||
va_list args;
|
||||
|
||||
btree_err_msg(&out, c, ca, b, i, k, b->written, write);
|
||||
|
||||
va_start(args, fmt);
|
||||
prt_vprintf(&out, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (write == WRITE) {
|
||||
bch2_print_string_as_lines(KERN_ERR, out.buf);
|
||||
ret = c->opts.errors == BCH_ON_ERROR_continue
|
||||
? 0
|
||||
: -BCH_ERR_fsck_errors_not_fixed;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!have_retry && ret == -BCH_ERR_btree_node_read_err_want_retry)
|
||||
ret = -BCH_ERR_btree_node_read_err_fixable;
|
||||
@ -576,6 +558,29 @@ static int __btree_err(int ret,
|
||||
if (!silent && ret != -BCH_ERR_btree_node_read_err_fixable)
|
||||
bch2_sb_error_count(c, err_type);
|
||||
|
||||
struct printbuf out = PRINTBUF;
|
||||
if (write != WRITE && ret != -BCH_ERR_btree_node_read_err_fixable) {
|
||||
printbuf_indent_add_nextline(&out, 2);
|
||||
#ifdef BCACHEFS_LOG_PREFIX
|
||||
prt_printf(&out, bch2_log_msg(c, ""));
|
||||
#endif
|
||||
}
|
||||
|
||||
btree_err_msg(&out, c, ca, b, i, k, b->written, write);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
prt_vprintf(&out, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (write == WRITE) {
|
||||
prt_str(&out, ", ");
|
||||
ret = __bch2_inconsistent_error(c, &out)
|
||||
? -BCH_ERR_fsck_errors_not_fixed
|
||||
: 0;
|
||||
silent = false;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case -BCH_ERR_btree_node_read_err_fixable:
|
||||
ret = !silent
|
||||
@ -585,25 +590,21 @@ static int __btree_err(int ret,
|
||||
ret != -BCH_ERR_fsck_ignore)
|
||||
goto fsck_err;
|
||||
ret = -BCH_ERR_fsck_fix;
|
||||
break;
|
||||
case -BCH_ERR_btree_node_read_err_want_retry:
|
||||
case -BCH_ERR_btree_node_read_err_must_retry:
|
||||
if (!silent)
|
||||
bch2_print_string_as_lines(KERN_ERR, out.buf);
|
||||
break;
|
||||
goto out;
|
||||
case -BCH_ERR_btree_node_read_err_bad_node:
|
||||
if (!silent)
|
||||
bch2_print_string_as_lines(KERN_ERR, out.buf);
|
||||
ret = bch2_topology_error(c);
|
||||
prt_str(&out, ", ");
|
||||
ret = __bch2_topology_error(c, &out);
|
||||
if (ret)
|
||||
silent = false;
|
||||
break;
|
||||
case -BCH_ERR_btree_node_read_err_incompatible:
|
||||
if (!silent)
|
||||
bch2_print_string_as_lines(KERN_ERR, out.buf);
|
||||
ret = -BCH_ERR_fsck_errors_not_fixed;
|
||||
silent = false;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (!silent)
|
||||
bch2_print_string_as_lines(KERN_ERR, out.buf);
|
||||
out:
|
||||
fsck_err:
|
||||
printbuf_exit(&out);
|
||||
@ -817,7 +818,7 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
|
||||
-BCH_ERR_btree_node_read_err_bad_node,
|
||||
c, ca, b, i, NULL,
|
||||
btree_node_bad_format,
|
||||
"invalid bkey format: %s\n %s", buf1.buf,
|
||||
"invalid bkey format: %s\n%s", buf1.buf,
|
||||
(printbuf_reset(&buf2),
|
||||
bch2_bkey_format_to_text(&buf2, &bn->format), buf2.buf));
|
||||
printbuf_reset(&buf1);
|
||||
@ -2117,8 +2118,14 @@ out:
|
||||
return;
|
||||
err:
|
||||
set_btree_node_noevict(b);
|
||||
bch2_fs_fatal_err_on(!bch2_err_matches(ret, EROFS), c,
|
||||
"writing btree node: %s", bch2_err_str(ret));
|
||||
|
||||
if (!bch2_err_matches(ret, EROFS)) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
prt_printf(&buf, "writing btree node: %s\n ", bch2_err_str(ret));
|
||||
bch2_btree_pos_to_text(&buf, c, b);
|
||||
bch2_fs_fatal_error(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -2135,10 +2142,14 @@ static void btree_node_write_endio(struct bio *bio)
|
||||
bch2_account_io_completion(ca, BCH_MEMBER_ERROR_write,
|
||||
wbio->submit_time, !bio->bi_status);
|
||||
|
||||
if (ca && bio->bi_status)
|
||||
bch_err_dev_ratelimited(ca,
|
||||
"btree write error: %s",
|
||||
bch2_blk_status_to_str(bio->bi_status));
|
||||
if (ca && bio->bi_status) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
prt_printf(&buf, "btree write error: %s\n ",
|
||||
bch2_blk_status_to_str(bio->bi_status));
|
||||
bch2_btree_pos_to_text(&buf, c, b);
|
||||
bch_err_dev_ratelimited(ca, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
|
||||
if (bio->bi_status) {
|
||||
unsigned long flags;
|
||||
|
@ -1487,22 +1487,14 @@ void bch2_trans_updates_to_text(struct printbuf *buf, struct btree_trans *trans)
|
||||
|
||||
for (struct jset_entry *e = trans->journal_entries;
|
||||
e != btree_trans_journal_entries_top(trans);
|
||||
e = vstruct_next(e))
|
||||
e = vstruct_next(e)) {
|
||||
bch2_journal_entry_to_text(buf, trans->c, e);
|
||||
prt_newline(buf);
|
||||
}
|
||||
|
||||
printbuf_indent_sub(buf, 2);
|
||||
}
|
||||
|
||||
noinline __cold
|
||||
void bch2_dump_trans_updates(struct btree_trans *trans)
|
||||
{
|
||||
struct printbuf buf = PRINTBUF;
|
||||
|
||||
bch2_trans_updates_to_text(&buf, trans);
|
||||
bch2_print_str(trans->c, buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
|
||||
static void bch2_btree_path_to_text_short(struct printbuf *out, struct btree_trans *trans, btree_path_idx_t path_idx)
|
||||
{
|
||||
struct btree_path *path = trans->paths + path_idx;
|
||||
|
@ -9,7 +9,6 @@
|
||||
void bch2_trans_updates_to_text(struct printbuf *, struct btree_trans *);
|
||||
void bch2_btree_path_to_text(struct printbuf *, struct btree_trans *, btree_path_idx_t);
|
||||
void bch2_trans_paths_to_text(struct printbuf *, struct btree_trans *);
|
||||
void bch2_dump_trans_updates(struct btree_trans *);
|
||||
void bch2_dump_trans_paths_updates(struct btree_trans *);
|
||||
|
||||
static inline int __bkey_err(const struct bkey *k)
|
||||
@ -335,13 +334,20 @@ static inline void bch2_trans_verify_not_unlocked_or_in_restart(struct btree_tra
|
||||
}
|
||||
|
||||
__always_inline
|
||||
static int btree_trans_restart_ip(struct btree_trans *trans, int err, unsigned long ip)
|
||||
static int btree_trans_restart_foreign_task(struct btree_trans *trans, int err, unsigned long ip)
|
||||
{
|
||||
BUG_ON(err <= 0);
|
||||
BUG_ON(!bch2_err_matches(-err, BCH_ERR_transaction_restart));
|
||||
|
||||
trans->restarted = err;
|
||||
trans->last_restarted_ip = ip;
|
||||
return -err;
|
||||
}
|
||||
|
||||
__always_inline
|
||||
static int btree_trans_restart_ip(struct btree_trans *trans, int err, unsigned long ip)
|
||||
{
|
||||
btree_trans_restart_foreign_task(trans, err, ip);
|
||||
#ifdef CONFIG_BCACHEFS_DEBUG
|
||||
darray_exit(&trans->last_restarted_trace);
|
||||
bch2_save_backtrace(&trans->last_restarted_trace, current, 0, GFP_NOWAIT);
|
||||
|
@ -644,6 +644,8 @@ void bch2_btree_and_journal_iter_init_node_iter(struct btree_trans *trans,
|
||||
*/
|
||||
static int journal_sort_key_cmp(const void *_l, const void *_r)
|
||||
{
|
||||
cond_resched();
|
||||
|
||||
const struct journal_key *l = _l;
|
||||
const struct journal_key *r = _r;
|
||||
|
||||
|
@ -91,10 +91,10 @@ static noinline void print_chain(struct printbuf *out, struct lock_graph *g)
|
||||
struct trans_waiting_for_lock *i;
|
||||
|
||||
for (i = g->g; i != g->g + g->nr; i++) {
|
||||
struct task_struct *task = i->trans->locking_wait.task;
|
||||
struct task_struct *task = READ_ONCE(i->trans->locking_wait.task);
|
||||
if (i != g->g)
|
||||
prt_str(out, "<- ");
|
||||
prt_printf(out, "%u ", task ?task->pid : 0);
|
||||
prt_printf(out, "%u ", task ? task->pid : 0);
|
||||
}
|
||||
prt_newline(out);
|
||||
}
|
||||
@ -172,7 +172,9 @@ static int abort_lock(struct lock_graph *g, struct trans_waiting_for_lock *i)
|
||||
{
|
||||
if (i == g->g) {
|
||||
trace_would_deadlock(g, i->trans);
|
||||
return btree_trans_restart(i->trans, BCH_ERR_transaction_restart_would_deadlock);
|
||||
return btree_trans_restart_foreign_task(i->trans,
|
||||
BCH_ERR_transaction_restart_would_deadlock,
|
||||
_THIS_IP_);
|
||||
} else {
|
||||
i->trans->lock_must_abort = true;
|
||||
wake_up_process(i->trans->locking_wait.task);
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/min_heap.h>
|
||||
#include <linux/sched/sysctl.h>
|
||||
#include <linux/sort.h>
|
||||
|
||||
struct find_btree_nodes_worker {
|
||||
@ -313,7 +314,8 @@ static int read_btree_nodes(struct find_btree_nodes *f)
|
||||
wake_up_process(t);
|
||||
}
|
||||
err:
|
||||
closure_sync(&cl);
|
||||
while (closure_sync_timeout(&cl, sysctl_hung_task_timeout_secs * HZ / 2))
|
||||
;
|
||||
return f->ret ?: ret;
|
||||
}
|
||||
|
||||
@ -577,10 +579,12 @@ int bch2_get_scanned_nodes(struct bch_fs *c, enum btree_id btree,
|
||||
|
||||
found_btree_node_to_key(&tmp.k, &n);
|
||||
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&tmp.k));
|
||||
bch_verbose(c, "%s(): recovering %s", __func__, buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
if (c->opts.verbose) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&tmp.k));
|
||||
bch_verbose(c, "%s(): recovering %s", __func__, buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
|
||||
BUG_ON(bch2_bkey_validate(c, bkey_i_to_s_c(&tmp.k),
|
||||
(struct bkey_validate_context) {
|
||||
|
@ -164,6 +164,7 @@ bool bch2_btree_bset_insert_key(struct btree_trans *trans,
|
||||
EBUG_ON(bpos_gt(insert->k.p, b->data->max_key));
|
||||
EBUG_ON(insert->k.u64s > bch2_btree_keys_u64s_remaining(b));
|
||||
EBUG_ON(!b->c.level && !bpos_eq(insert->k.p, path->pos));
|
||||
kmsan_check_memory(insert, bkey_bytes(&insert->k));
|
||||
|
||||
k = bch2_btree_node_iter_peek_all(node_iter, b);
|
||||
if (k && bkey_cmp_left_packed(b, k, &insert->k.p))
|
||||
|
@ -512,6 +512,8 @@ static noinline int bch2_trans_update_get_key_cache(struct btree_trans *trans,
|
||||
int __must_check bch2_trans_update(struct btree_trans *trans, struct btree_iter *iter,
|
||||
struct bkey_i *k, enum btree_iter_update_trigger_flags flags)
|
||||
{
|
||||
kmsan_check_memory(k, bkey_bytes(&k->k));
|
||||
|
||||
btree_path_idx_t path_idx = iter->update_path ?: iter->path;
|
||||
int ret;
|
||||
|
||||
@ -844,6 +846,19 @@ int bch2_trans_log_msg(struct btree_trans *trans, struct printbuf *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bch2_trans_log_bkey(struct btree_trans *trans, enum btree_id btree,
|
||||
unsigned level, struct bkey_i *k)
|
||||
{
|
||||
struct jset_entry *e = bch2_trans_jset_entry_alloc(trans, jset_u64s(k->k.u64s));
|
||||
int ret = PTR_ERR_OR_ZERO(e);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
journal_entry_init(e, BCH_JSET_ENTRY_log_bkey, btree, level, k->k.u64s);
|
||||
bkey_copy(e->start, k);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__printf(3, 0)
|
||||
static int
|
||||
__bch2_fs_log_msg(struct bch_fs *c, unsigned commit_flags, const char *fmt,
|
||||
|
@ -133,6 +133,8 @@ static inline int __must_check bch2_trans_update_buffered(struct btree_trans *tr
|
||||
enum btree_id btree,
|
||||
struct bkey_i *k)
|
||||
{
|
||||
kmsan_check_memory(k, bkey_bytes(&k->k));
|
||||
|
||||
if (unlikely(!btree_type_uses_write_buffer(btree))) {
|
||||
int ret = bch2_btree_write_buffer_insert_err(trans, btree, k);
|
||||
dump_stack();
|
||||
@ -168,6 +170,8 @@ void bch2_trans_commit_hook(struct btree_trans *,
|
||||
int __bch2_trans_commit(struct btree_trans *, unsigned);
|
||||
|
||||
int bch2_trans_log_msg(struct btree_trans *, struct printbuf *);
|
||||
int bch2_trans_log_bkey(struct btree_trans *, enum btree_id, unsigned, struct bkey_i *);
|
||||
|
||||
__printf(2, 3) int bch2_fs_log_msg(struct bch_fs *, const char *, ...);
|
||||
__printf(2, 3) int bch2_journal_log_msg(struct bch_fs *, const char *, ...);
|
||||
|
||||
|
@ -35,6 +35,8 @@ static const char * const bch2_btree_update_modes[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static void bch2_btree_update_to_text(struct printbuf *, struct btree_update *);
|
||||
|
||||
static int bch2_btree_insert_node(struct btree_update *, struct btree_trans *,
|
||||
btree_path_idx_t, struct btree *, struct keylist *);
|
||||
static void bch2_btree_update_add_new_node(struct btree_update *, struct btree *);
|
||||
@ -54,6 +56,8 @@ int bch2_btree_node_check_topology(struct btree_trans *trans, struct btree *b)
|
||||
struct bkey_buf prev;
|
||||
int ret = 0;
|
||||
|
||||
printbuf_indent_add_nextline(&buf, 2);
|
||||
|
||||
BUG_ON(b->key.k.type == KEY_TYPE_btree_ptr_v2 &&
|
||||
!bpos_eq(bkey_i_to_btree_ptr_v2(&b->key)->v.min_key,
|
||||
b->data->min_key));
|
||||
@ -64,19 +68,20 @@ int bch2_btree_node_check_topology(struct btree_trans *trans, struct btree *b)
|
||||
|
||||
if (b == btree_node_root(c, b)) {
|
||||
if (!bpos_eq(b->data->min_key, POS_MIN)) {
|
||||
printbuf_reset(&buf);
|
||||
ret = __bch2_topology_error(c, &buf);
|
||||
|
||||
bch2_bpos_to_text(&buf, b->data->min_key);
|
||||
log_fsck_err(trans, btree_root_bad_min_key,
|
||||
"btree root with incorrect min_key: %s", buf.buf);
|
||||
goto topology_repair;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!bpos_eq(b->data->max_key, SPOS_MAX)) {
|
||||
printbuf_reset(&buf);
|
||||
ret = __bch2_topology_error(c, &buf);
|
||||
bch2_bpos_to_text(&buf, b->data->max_key);
|
||||
log_fsck_err(trans, btree_root_bad_max_key,
|
||||
"btree root with incorrect max_key: %s", buf.buf);
|
||||
goto topology_repair;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,20 +99,19 @@ int bch2_btree_node_check_topology(struct btree_trans *trans, struct btree *b)
|
||||
: bpos_successor(prev.k->k.p);
|
||||
|
||||
if (!bpos_eq(expected_min, bp.v->min_key)) {
|
||||
bch2_topology_error(c);
|
||||
ret = __bch2_topology_error(c, &buf);
|
||||
|
||||
printbuf_reset(&buf);
|
||||
prt_str(&buf, "end of prev node doesn't match start of next node\n in ");
|
||||
prt_str(&buf, "end of prev node doesn't match start of next node\nin ");
|
||||
bch2_btree_id_level_to_text(&buf, b->c.btree_id, b->c.level);
|
||||
prt_str(&buf, " node ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
|
||||
prt_str(&buf, "\n prev ");
|
||||
prt_str(&buf, "\nprev ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(prev.k));
|
||||
prt_str(&buf, "\n next ");
|
||||
prt_str(&buf, "\nnext ");
|
||||
bch2_bkey_val_to_text(&buf, c, k);
|
||||
|
||||
log_fsck_err(trans, btree_node_topology_bad_min_key, "%s", buf.buf);
|
||||
goto topology_repair;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bch2_bkey_buf_reassemble(&prev, c, k);
|
||||
@ -115,29 +119,25 @@ int bch2_btree_node_check_topology(struct btree_trans *trans, struct btree *b)
|
||||
}
|
||||
|
||||
if (bkey_deleted(&prev.k->k)) {
|
||||
bch2_topology_error(c);
|
||||
ret = __bch2_topology_error(c, &buf);
|
||||
|
||||
printbuf_reset(&buf);
|
||||
prt_str(&buf, "empty interior node\n in ");
|
||||
prt_str(&buf, "empty interior node\nin ");
|
||||
bch2_btree_id_level_to_text(&buf, b->c.btree_id, b->c.level);
|
||||
prt_str(&buf, " node ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
|
||||
|
||||
log_fsck_err(trans, btree_node_topology_empty_interior_node, "%s", buf.buf);
|
||||
goto topology_repair;
|
||||
} else if (!bpos_eq(prev.k->k.p, b->key.k.p)) {
|
||||
bch2_topology_error(c);
|
||||
ret = __bch2_topology_error(c, &buf);
|
||||
|
||||
printbuf_reset(&buf);
|
||||
prt_str(&buf, "last child node doesn't end at end of parent node\n in ");
|
||||
prt_str(&buf, "last child node doesn't end at end of parent node\nin ");
|
||||
bch2_btree_id_level_to_text(&buf, b->c.btree_id, b->c.level);
|
||||
prt_str(&buf, " node ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
|
||||
prt_str(&buf, "\n last key ");
|
||||
prt_str(&buf, "\nlast key ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(prev.k));
|
||||
|
||||
log_fsck_err(trans, btree_node_topology_bad_max_key, "%s", buf.buf);
|
||||
goto topology_repair;
|
||||
}
|
||||
out:
|
||||
fsck_err:
|
||||
@ -145,9 +145,6 @@ fsck_err:
|
||||
bch2_bkey_buf_exit(&prev, c);
|
||||
printbuf_exit(&buf);
|
||||
return ret;
|
||||
topology_repair:
|
||||
ret = bch2_topology_error(c);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Calculate ideal packed bkey format for new btree nodes: */
|
||||
@ -649,6 +646,14 @@ static int btree_update_nodes_written_trans(struct btree_trans *trans,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the node has been reused, we might be reading uninitialized memory - that's fine: */
|
||||
static noinline __no_kmsan_checks bool btree_node_seq_matches(struct btree *b, __le64 seq)
|
||||
{
|
||||
struct btree_node *b_data = READ_ONCE(b->data);
|
||||
|
||||
return (b_data ? b_data->keys.seq : 0) == seq;
|
||||
}
|
||||
|
||||
static void btree_update_nodes_written(struct btree_update *as)
|
||||
{
|
||||
struct bch_fs *c = as->c;
|
||||
@ -677,17 +682,9 @@ static void btree_update_nodes_written(struct btree_update *as)
|
||||
* on disk:
|
||||
*/
|
||||
for (i = 0; i < as->nr_old_nodes; i++) {
|
||||
__le64 seq;
|
||||
|
||||
b = as->old_nodes[i];
|
||||
|
||||
bch2_trans_begin(trans);
|
||||
btree_node_lock_nopath_nofail(trans, &b->c, SIX_LOCK_read);
|
||||
seq = b->data ? b->data->keys.seq : 0;
|
||||
six_unlock_read(&b->c.lock);
|
||||
bch2_trans_unlock_long(trans);
|
||||
|
||||
if (seq == as->old_nodes_seq[i])
|
||||
if (btree_node_seq_matches(b, as->old_nodes_seq[i]))
|
||||
wait_on_bit_io(&b->flags, BTREE_NODE_write_in_flight_inner,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
}
|
||||
@ -1271,7 +1268,8 @@ err:
|
||||
bch2_btree_update_free(as, trans);
|
||||
if (!bch2_err_matches(ret, ENOSPC) &&
|
||||
!bch2_err_matches(ret, EROFS) &&
|
||||
ret != -BCH_ERR_journal_reclaim_would_deadlock)
|
||||
ret != -BCH_ERR_journal_reclaim_would_deadlock &&
|
||||
ret != -BCH_ERR_journal_shutdown)
|
||||
bch_err_fn_ratelimited(c, ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
@ -1782,11 +1780,24 @@ static int bch2_btree_insert_node(struct btree_update *as, struct btree_trans *t
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&c->gc_lock);
|
||||
BUG_ON(!btree_node_intent_locked(path, b->c.level));
|
||||
BUG_ON(!b->c.level);
|
||||
BUG_ON(!as || as->b);
|
||||
bch2_verify_keylist_sorted(keys);
|
||||
|
||||
if (!btree_node_intent_locked(path, b->c.level)) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_log_msg_start(c, &buf);
|
||||
prt_printf(&buf, "%s(): node not locked at level %u\n",
|
||||
__func__, b->c.level);
|
||||
bch2_btree_update_to_text(&buf, as);
|
||||
bch2_btree_path_to_text(&buf, trans, path_idx);
|
||||
|
||||
bch2_print_string_as_lines(KERN_ERR, buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
bch2_fs_emergency_read_only(c);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = bch2_btree_node_lock_write(trans, path, &b->c);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -2007,18 +2018,22 @@ int __bch2_foreground_maybe_merge(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
if (!bpos_eq(bpos_successor(prev->data->max_key), next->data->min_key)) {
|
||||
struct printbuf buf1 = PRINTBUF, buf2 = PRINTBUF;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
|
||||
bch2_bpos_to_text(&buf1, prev->data->max_key);
|
||||
bch2_bpos_to_text(&buf2, next->data->min_key);
|
||||
bch_err(c,
|
||||
"%s(): btree topology error:\n"
|
||||
" prev ends at %s\n"
|
||||
" next starts at %s",
|
||||
__func__, buf1.buf, buf2.buf);
|
||||
printbuf_exit(&buf1);
|
||||
printbuf_exit(&buf2);
|
||||
ret = bch2_topology_error(c);
|
||||
printbuf_indent_add_nextline(&buf, 2);
|
||||
prt_printf(&buf, "%s(): ", __func__);
|
||||
ret = __bch2_topology_error(c, &buf);
|
||||
prt_newline(&buf);
|
||||
|
||||
prt_printf(&buf, "prev ends at ");
|
||||
bch2_bpos_to_text(&buf, prev->data->max_key);
|
||||
prt_newline(&buf);
|
||||
|
||||
prt_printf(&buf, "next starts at ");
|
||||
bch2_bpos_to_text(&buf, next->data->min_key);
|
||||
|
||||
bch_err(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -2288,7 +2303,9 @@ static void async_btree_node_rewrite_work(struct work_struct *work)
|
||||
|
||||
int ret = bch2_trans_do(c, bch2_btree_node_rewrite_key(trans,
|
||||
a->btree_id, a->level, a->key.k, 0));
|
||||
if (ret != -ENOENT)
|
||||
if (ret != -ENOENT &&
|
||||
!bch2_err_matches(ret, EROFS) &&
|
||||
ret != -BCH_ERR_journal_shutdown)
|
||||
bch_err_fn_ratelimited(c, ret);
|
||||
|
||||
spin_lock(&c->btree_node_rewrites_lock);
|
||||
|
@ -381,6 +381,36 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bucket_ref_update_err(struct btree_trans *trans, struct printbuf *buf,
|
||||
struct bkey_s_c k, bool insert, enum bch_sb_error_id id)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
bool repeat = false, print = true, suppress = false;
|
||||
|
||||
prt_printf(buf, "\nwhile marking ");
|
||||
bch2_bkey_val_to_text(buf, c, k);
|
||||
prt_newline(buf);
|
||||
|
||||
__bch2_count_fsck_err(c, id, buf->buf, &repeat, &print, &suppress);
|
||||
|
||||
int ret = bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_check_allocations);
|
||||
|
||||
if (insert) {
|
||||
print = true;
|
||||
suppress = false;
|
||||
|
||||
bch2_trans_updates_to_text(buf, trans);
|
||||
__bch2_inconsistent_error(c, buf);
|
||||
ret = -BCH_ERR_bucket_ref_update;
|
||||
}
|
||||
|
||||
if (suppress)
|
||||
prt_printf(buf, "Ratelimiting new instances of previous error\n");
|
||||
if (print)
|
||||
bch2_print_string_as_lines(KERN_ERR, buf->buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bch2_bucket_ref_update(struct btree_trans *trans, struct bch_dev *ca,
|
||||
struct bkey_s_c k,
|
||||
const struct bch_extent_ptr *ptr,
|
||||
@ -396,32 +426,29 @@ int bch2_bucket_ref_update(struct btree_trans *trans, struct bch_dev *ca,
|
||||
|
||||
BUG_ON(!sectors);
|
||||
|
||||
if (gen_after(ptr->gen, b_gen)) {
|
||||
bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_check_allocations);
|
||||
log_fsck_err(trans, ptr_gen_newer_than_bucket_gen,
|
||||
"bucket %u:%zu gen %u data type %s: ptr gen %u newer than bucket gen\n"
|
||||
"while marking %s",
|
||||
if (unlikely(gen_after(ptr->gen, b_gen))) {
|
||||
bch2_log_msg_start(c, &buf);
|
||||
prt_printf(&buf,
|
||||
"bucket %u:%zu gen %u data type %s: ptr gen %u newer than bucket gen",
|
||||
ptr->dev, bucket_nr, b_gen,
|
||||
bch2_data_type_str(bucket_data_type ?: ptr_data_type),
|
||||
ptr->gen,
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf));
|
||||
if (inserting)
|
||||
goto err;
|
||||
ptr->gen);
|
||||
|
||||
ret = bucket_ref_update_err(trans, &buf, k, inserting,
|
||||
BCH_FSCK_ERR_ptr_gen_newer_than_bucket_gen);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (gen_cmp(b_gen, ptr->gen) > BUCKET_GC_GEN_MAX) {
|
||||
bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_check_allocations);
|
||||
log_fsck_err(trans, ptr_too_stale,
|
||||
"bucket %u:%zu gen %u data type %s: ptr gen %u too stale\n"
|
||||
"while marking %s",
|
||||
if (unlikely(gen_cmp(b_gen, ptr->gen) > BUCKET_GC_GEN_MAX)) {
|
||||
bch2_log_msg_start(c, &buf);
|
||||
prt_printf(&buf,
|
||||
"bucket %u:%zu gen %u data type %s: ptr gen %u too stale",
|
||||
ptr->dev, bucket_nr, b_gen,
|
||||
bch2_data_type_str(bucket_data_type ?: ptr_data_type),
|
||||
ptr->gen,
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k), buf.buf));
|
||||
if (inserting)
|
||||
goto err;
|
||||
ptr->gen);
|
||||
|
||||
ret = bucket_ref_update_err(trans, &buf, k, inserting,
|
||||
BCH_FSCK_ERR_ptr_too_stale);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -430,62 +457,50 @@ int bch2_bucket_ref_update(struct btree_trans *trans, struct bch_dev *ca,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (b_gen != ptr->gen) {
|
||||
bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_check_allocations);
|
||||
log_fsck_err(trans, stale_dirty_ptr,
|
||||
"bucket %u:%zu gen %u (mem gen %u) data type %s: stale dirty ptr (gen %u)\n"
|
||||
"while marking %s",
|
||||
if (unlikely(b_gen != ptr->gen)) {
|
||||
bch2_log_msg_start(c, &buf);
|
||||
prt_printf(&buf,
|
||||
"bucket %u:%zu gen %u (mem gen %u) data type %s: stale dirty ptr (gen %u)",
|
||||
ptr->dev, bucket_nr, b_gen,
|
||||
bucket_gen_get(ca, bucket_nr),
|
||||
bch2_data_type_str(bucket_data_type ?: ptr_data_type),
|
||||
ptr->gen,
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k), buf.buf));
|
||||
if (inserting)
|
||||
goto err;
|
||||
ptr->gen);
|
||||
|
||||
ret = bucket_ref_update_err(trans, &buf, k, inserting,
|
||||
BCH_FSCK_ERR_stale_dirty_ptr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bucket_data_type_mismatch(bucket_data_type, ptr_data_type)) {
|
||||
bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_check_allocations);
|
||||
log_fsck_err(trans, ptr_bucket_data_type_mismatch,
|
||||
"bucket %u:%zu gen %u different types of data in same bucket: %s, %s\n"
|
||||
"while marking %s",
|
||||
ptr->dev, bucket_nr, b_gen,
|
||||
bch2_data_type_str(bucket_data_type),
|
||||
bch2_data_type_str(ptr_data_type),
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k), buf.buf));
|
||||
if (inserting)
|
||||
goto err;
|
||||
if (unlikely(bucket_data_type_mismatch(bucket_data_type, ptr_data_type))) {
|
||||
bch2_log_msg_start(c, &buf);
|
||||
prt_printf(&buf, "bucket %u:%zu gen %u different types of data in same bucket: %s, %s",
|
||||
ptr->dev, bucket_nr, b_gen,
|
||||
bch2_data_type_str(bucket_data_type),
|
||||
bch2_data_type_str(ptr_data_type));
|
||||
|
||||
ret = bucket_ref_update_err(trans, &buf, k, inserting,
|
||||
BCH_FSCK_ERR_ptr_bucket_data_type_mismatch);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((u64) *bucket_sectors + sectors > U32_MAX) {
|
||||
bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_check_allocations);
|
||||
log_fsck_err(trans, bucket_sector_count_overflow,
|
||||
"bucket %u:%zu gen %u data type %s sector count overflow: %u + %lli > U32_MAX\n"
|
||||
"while marking %s",
|
||||
if (unlikely((u64) *bucket_sectors + sectors > U32_MAX)) {
|
||||
bch2_log_msg_start(c, &buf);
|
||||
prt_printf(&buf,
|
||||
"bucket %u:%zu gen %u data type %s sector count overflow: %u + %lli > U32_MAX",
|
||||
ptr->dev, bucket_nr, b_gen,
|
||||
bch2_data_type_str(bucket_data_type ?: ptr_data_type),
|
||||
*bucket_sectors, sectors,
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k), buf.buf));
|
||||
if (inserting)
|
||||
goto err;
|
||||
*bucket_sectors, sectors);
|
||||
|
||||
ret = bucket_ref_update_err(trans, &buf, k, inserting,
|
||||
BCH_FSCK_ERR_bucket_sector_count_overflow);
|
||||
sectors = -*bucket_sectors;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*bucket_sectors += sectors;
|
||||
out:
|
||||
printbuf_exit(&buf);
|
||||
return ret;
|
||||
err:
|
||||
fsck_err:
|
||||
bch2_dump_trans_updates(trans);
|
||||
bch2_inconsistent_error(c);
|
||||
ret = -BCH_ERR_bucket_ref_update;
|
||||
goto out;
|
||||
}
|
||||
|
||||
void bch2_trans_account_disk_usage_change(struct btree_trans *trans)
|
||||
@ -651,9 +666,9 @@ static int bch2_trigger_stripe_ptr(struct btree_trans *trans,
|
||||
stripe_blockcount_get(&s->v, p.ec.block) +
|
||||
sectors);
|
||||
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_replicas,
|
||||
};
|
||||
struct disk_accounting_pos acc;
|
||||
memset(&acc, 0, sizeof(acc));
|
||||
acc.type = BCH_DISK_ACCOUNTING_replicas;
|
||||
bch2_bkey_to_replicas(&acc.replicas, bkey_i_to_s_c(&s->k_i));
|
||||
acc.replicas.data_type = data_type;
|
||||
ret = bch2_disk_accounting_mod(trans, &acc, §ors, 1, false);
|
||||
@ -677,19 +692,21 @@ err:
|
||||
if (!m || !m->alive) {
|
||||
gc_stripe_unlock(m);
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_log_msg_start(c, &buf);
|
||||
prt_printf(&buf, "pointer to nonexistent stripe %llu\n while marking ",
|
||||
(u64) p.ec.idx);
|
||||
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);
|
||||
__bch2_inconsistent_error(c, &buf);
|
||||
bch2_print_string_as_lines(KERN_ERR, buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
bch2_inconsistent_error(c);
|
||||
return -BCH_ERR_trigger_stripe_pointer;
|
||||
}
|
||||
|
||||
m->block_sectors[p.ec.block] += sectors;
|
||||
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_replicas,
|
||||
};
|
||||
struct disk_accounting_pos acc;
|
||||
memset(&acc, 0, sizeof(acc));
|
||||
acc.type = BCH_DISK_ACCOUNTING_replicas;
|
||||
memcpy(&acc.replicas, &m->r.e, replicas_entry_bytes(&m->r.e));
|
||||
gc_stripe_unlock(m);
|
||||
|
||||
@ -717,16 +734,14 @@ static int __trigger_extent(struct btree_trans *trans,
|
||||
: BCH_DATA_user;
|
||||
int ret = 0;
|
||||
|
||||
struct disk_accounting_pos acc_replicas_key = {
|
||||
.type = BCH_DISK_ACCOUNTING_replicas,
|
||||
.replicas.data_type = data_type,
|
||||
.replicas.nr_devs = 0,
|
||||
.replicas.nr_required = 1,
|
||||
};
|
||||
struct disk_accounting_pos acc_replicas_key;
|
||||
memset(&acc_replicas_key, 0, sizeof(acc_replicas_key));
|
||||
acc_replicas_key.type = BCH_DISK_ACCOUNTING_replicas;
|
||||
acc_replicas_key.replicas.data_type = data_type;
|
||||
acc_replicas_key.replicas.nr_devs = 0;
|
||||
acc_replicas_key.replicas.nr_required = 1;
|
||||
|
||||
struct disk_accounting_pos acct_compression_key = {
|
||||
.type = BCH_DISK_ACCOUNTING_compression,
|
||||
};
|
||||
unsigned cur_compression_type = 0;
|
||||
u64 compression_acct[3] = { 1, 0, 0 };
|
||||
|
||||
bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
|
||||
@ -760,13 +775,13 @@ static int __trigger_extent(struct btree_trans *trans,
|
||||
acc_replicas_key.replicas.nr_required = 0;
|
||||
}
|
||||
|
||||
if (acct_compression_key.compression.type &&
|
||||
acct_compression_key.compression.type != p.crc.compression_type) {
|
||||
if (cur_compression_type &&
|
||||
cur_compression_type != p.crc.compression_type) {
|
||||
if (flags & BTREE_TRIGGER_overwrite)
|
||||
bch2_u64s_neg(compression_acct, ARRAY_SIZE(compression_acct));
|
||||
|
||||
ret = bch2_disk_accounting_mod(trans, &acct_compression_key, compression_acct,
|
||||
ARRAY_SIZE(compression_acct), gc);
|
||||
ret = bch2_disk_accounting_mod2(trans, gc, compression_acct,
|
||||
compression, cur_compression_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -775,7 +790,7 @@ static int __trigger_extent(struct btree_trans *trans,
|
||||
compression_acct[2] = 0;
|
||||
}
|
||||
|
||||
acct_compression_key.compression.type = p.crc.compression_type;
|
||||
cur_compression_type = p.crc.compression_type;
|
||||
if (p.crc.compression_type) {
|
||||
compression_acct[1] += p.crc.uncompressed_size;
|
||||
compression_acct[2] += p.crc.compressed_size;
|
||||
@ -789,45 +804,34 @@ static int __trigger_extent(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
if (acc_replicas_key.replicas.nr_devs && !level && k.k->p.snapshot) {
|
||||
struct disk_accounting_pos acc_snapshot_key = {
|
||||
.type = BCH_DISK_ACCOUNTING_snapshot,
|
||||
.snapshot.id = k.k->p.snapshot,
|
||||
};
|
||||
ret = bch2_disk_accounting_mod(trans, &acc_snapshot_key, replicas_sectors, 1, gc);
|
||||
ret = bch2_disk_accounting_mod2_nr(trans, gc, replicas_sectors, 1, snapshot, k.k->p.snapshot);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (acct_compression_key.compression.type) {
|
||||
if (cur_compression_type) {
|
||||
if (flags & BTREE_TRIGGER_overwrite)
|
||||
bch2_u64s_neg(compression_acct, ARRAY_SIZE(compression_acct));
|
||||
|
||||
ret = bch2_disk_accounting_mod(trans, &acct_compression_key, compression_acct,
|
||||
ARRAY_SIZE(compression_acct), gc);
|
||||
ret = bch2_disk_accounting_mod2(trans, gc, compression_acct,
|
||||
compression, cur_compression_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (level) {
|
||||
struct disk_accounting_pos acc_btree_key = {
|
||||
.type = BCH_DISK_ACCOUNTING_btree,
|
||||
.btree.id = btree_id,
|
||||
};
|
||||
ret = bch2_disk_accounting_mod(trans, &acc_btree_key, replicas_sectors, 1, gc);
|
||||
ret = bch2_disk_accounting_mod2_nr(trans, gc, replicas_sectors, 1, btree, btree_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
bool insert = !(flags & BTREE_TRIGGER_overwrite);
|
||||
struct disk_accounting_pos acc_inum_key = {
|
||||
.type = BCH_DISK_ACCOUNTING_inum,
|
||||
.inum.inum = k.k->p.inode,
|
||||
};
|
||||
|
||||
s64 v[3] = {
|
||||
insert ? 1 : -1,
|
||||
insert ? k.k->size : -((s64) k.k->size),
|
||||
*replicas_sectors,
|
||||
};
|
||||
ret = bch2_disk_accounting_mod(trans, &acc_inum_key, v, ARRAY_SIZE(v), gc);
|
||||
ret = bch2_disk_accounting_mod2(trans, gc, v, inum, k.k->p.inode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -876,15 +880,15 @@ int bch2_trigger_extent(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
int need_rebalance_delta = 0;
|
||||
s64 need_rebalance_sectors_delta = 0;
|
||||
s64 need_rebalance_sectors_delta[1] = { 0 };
|
||||
|
||||
s64 s = bch2_bkey_sectors_need_rebalance(c, old);
|
||||
need_rebalance_delta -= s != 0;
|
||||
need_rebalance_sectors_delta -= s;
|
||||
need_rebalance_sectors_delta[0] -= s;
|
||||
|
||||
s = bch2_bkey_sectors_need_rebalance(c, new.s_c);
|
||||
need_rebalance_delta += s != 0;
|
||||
need_rebalance_sectors_delta += s;
|
||||
need_rebalance_sectors_delta[0] += s;
|
||||
|
||||
if ((flags & BTREE_TRIGGER_transactional) && need_rebalance_delta) {
|
||||
int ret = bch2_btree_bit_mod_buffered(trans, BTREE_ID_rebalance_work,
|
||||
@ -893,12 +897,9 @@ int bch2_trigger_extent(struct btree_trans *trans,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (need_rebalance_sectors_delta) {
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_rebalance_work,
|
||||
};
|
||||
int ret = bch2_disk_accounting_mod(trans, &acc, &need_rebalance_sectors_delta, 1,
|
||||
flags & BTREE_TRIGGER_gc);
|
||||
if (need_rebalance_sectors_delta[0]) {
|
||||
int ret = bch2_disk_accounting_mod2(trans, flags & BTREE_TRIGGER_gc,
|
||||
need_rebalance_sectors_delta, rebalance_work);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -914,17 +915,13 @@ static int __trigger_reservation(struct btree_trans *trans,
|
||||
enum btree_iter_update_trigger_flags flags)
|
||||
{
|
||||
if (flags & (BTREE_TRIGGER_transactional|BTREE_TRIGGER_gc)) {
|
||||
s64 sectors = k.k->size;
|
||||
s64 sectors[1] = { k.k->size };
|
||||
|
||||
if (flags & BTREE_TRIGGER_overwrite)
|
||||
sectors = -sectors;
|
||||
sectors[0] = -sectors[0];
|
||||
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_persistent_reserved,
|
||||
.persistent_reserved.nr_replicas = bkey_s_c_to_reservation(k).v->nr_replicas,
|
||||
};
|
||||
|
||||
return bch2_disk_accounting_mod(trans, &acc, §ors, 1, flags & BTREE_TRIGGER_gc);
|
||||
return bch2_disk_accounting_mod2(trans, flags & BTREE_TRIGGER_gc, sectors,
|
||||
persistent_reserved, bkey_s_c_to_reservation(k).v->nr_replicas);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -426,10 +426,8 @@ static long bch2_ioctl_fs_usage(struct bch_fs *c,
|
||||
arg.replica_entries_bytes = replicas.nr;
|
||||
|
||||
for (unsigned i = 0; i < BCH_REPLICAS_MAX; i++) {
|
||||
struct disk_accounting_pos k = {
|
||||
.type = BCH_DISK_ACCOUNTING_persistent_reserved,
|
||||
.persistent_reserved.nr_replicas = i,
|
||||
};
|
||||
struct disk_accounting_pos k;
|
||||
disk_accounting_key_init(k, persistent_reserved, .nr_replicas = i);
|
||||
|
||||
bch2_accounting_mem_read(c,
|
||||
disk_accounting_pos_to_bpos(&k),
|
||||
|
@ -466,7 +466,7 @@ int bch2_rechecksum_bio(struct bch_fs *c, struct bio *bio,
|
||||
prt_str(&buf, ")");
|
||||
WARN_RATELIMIT(1, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
return -EIO;
|
||||
return -BCH_ERR_recompute_checksum;
|
||||
}
|
||||
|
||||
for (i = splits; i < splits + ARRAY_SIZE(splits); i++) {
|
||||
|
@ -177,7 +177,7 @@ static int __bio_uncompress(struct bch_fs *c, struct bio *src,
|
||||
size_t src_len = src->bi_iter.bi_size;
|
||||
size_t dst_len = crc.uncompressed_size << 9;
|
||||
void *workspace;
|
||||
int ret;
|
||||
int ret = 0, ret2;
|
||||
|
||||
enum bch_compression_opts opt = bch2_compression_type_to_opt(crc.compression_type);
|
||||
mempool_t *workspace_pool = &c->compress_workspace[opt];
|
||||
@ -189,7 +189,7 @@ static int __bio_uncompress(struct bch_fs *c, struct bio *src,
|
||||
else
|
||||
ret = -BCH_ERR_compression_workspace_not_initialized;
|
||||
if (ret)
|
||||
goto out;
|
||||
goto err;
|
||||
}
|
||||
|
||||
src_data = bio_map_or_bounce(c, src, READ);
|
||||
@ -197,10 +197,10 @@ static int __bio_uncompress(struct bch_fs *c, struct bio *src,
|
||||
switch (crc.compression_type) {
|
||||
case BCH_COMPRESSION_TYPE_lz4_old:
|
||||
case BCH_COMPRESSION_TYPE_lz4:
|
||||
ret = LZ4_decompress_safe_partial(src_data.b, dst_data,
|
||||
src_len, dst_len, dst_len);
|
||||
if (ret != dst_len)
|
||||
goto err;
|
||||
ret2 = LZ4_decompress_safe_partial(src_data.b, dst_data,
|
||||
src_len, dst_len, dst_len);
|
||||
if (ret2 != dst_len)
|
||||
ret = -BCH_ERR_decompress_lz4;
|
||||
break;
|
||||
case BCH_COMPRESSION_TYPE_gzip: {
|
||||
z_stream strm = {
|
||||
@ -214,45 +214,43 @@ static int __bio_uncompress(struct bch_fs *c, struct bio *src,
|
||||
|
||||
zlib_set_workspace(&strm, workspace);
|
||||
zlib_inflateInit2(&strm, -MAX_WBITS);
|
||||
ret = zlib_inflate(&strm, Z_FINISH);
|
||||
ret2 = zlib_inflate(&strm, Z_FINISH);
|
||||
|
||||
mempool_free(workspace, workspace_pool);
|
||||
|
||||
if (ret != Z_STREAM_END)
|
||||
goto err;
|
||||
if (ret2 != Z_STREAM_END)
|
||||
ret = -BCH_ERR_decompress_gzip;
|
||||
break;
|
||||
}
|
||||
case BCH_COMPRESSION_TYPE_zstd: {
|
||||
ZSTD_DCtx *ctx;
|
||||
size_t real_src_len = le32_to_cpup(src_data.b);
|
||||
|
||||
if (real_src_len > src_len - 4)
|
||||
if (real_src_len > src_len - 4) {
|
||||
ret = -BCH_ERR_decompress_zstd_src_len_bad;
|
||||
goto err;
|
||||
}
|
||||
|
||||
workspace = mempool_alloc(workspace_pool, GFP_NOFS);
|
||||
ctx = zstd_init_dctx(workspace, zstd_dctx_workspace_bound());
|
||||
|
||||
ret = zstd_decompress_dctx(ctx,
|
||||
ret2 = zstd_decompress_dctx(ctx,
|
||||
dst_data, dst_len,
|
||||
src_data.b + 4, real_src_len);
|
||||
|
||||
mempool_free(workspace, workspace_pool);
|
||||
|
||||
if (ret != dst_len)
|
||||
goto err;
|
||||
if (ret2 != dst_len)
|
||||
ret = -BCH_ERR_decompress_zstd;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
ret = 0;
|
||||
err:
|
||||
fsck_err:
|
||||
out:
|
||||
bio_unmap_or_unbounce(c, src_data);
|
||||
return ret;
|
||||
err:
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
int bch2_bio_uncompress_inplace(struct bch_write_op *op,
|
||||
@ -268,27 +266,22 @@ int bch2_bio_uncompress_inplace(struct bch_write_op *op,
|
||||
BUG_ON(!bio->bi_vcnt);
|
||||
BUG_ON(DIV_ROUND_UP(crc->live_size, PAGE_SECTORS) > bio->bi_max_vecs);
|
||||
|
||||
if (crc->uncompressed_size << 9 > c->opts.encoded_extent_max ||
|
||||
crc->compressed_size << 9 > c->opts.encoded_extent_max) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_write_op_error(&buf, op, op->pos.offset,
|
||||
"extent too big to decompress");
|
||||
bch_err_ratelimited(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
return -EIO;
|
||||
if (crc->uncompressed_size << 9 > c->opts.encoded_extent_max) {
|
||||
bch2_write_op_error(op, op->pos.offset,
|
||||
"extent too big to decompress (%u > %u)",
|
||||
crc->uncompressed_size << 9, c->opts.encoded_extent_max);
|
||||
return -BCH_ERR_decompress_exceeded_max_encoded_extent;
|
||||
}
|
||||
|
||||
data = __bounce_alloc(c, dst_len, WRITE);
|
||||
|
||||
if (__bio_uncompress(c, bio, data.b, *crc)) {
|
||||
if (!c->opts.no_data_io) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_write_op_error(&buf, op, op->pos.offset,
|
||||
"decompression error");
|
||||
bch_err_ratelimited(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
ret = -EIO;
|
||||
ret = __bio_uncompress(c, bio, data.b, *crc);
|
||||
|
||||
if (c->opts.no_data_io)
|
||||
ret = 0;
|
||||
|
||||
if (ret) {
|
||||
bch2_write_op_error(op, op->pos.offset, "%s", bch2_err_str(ret));
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -321,7 +314,7 @@ int bch2_bio_uncompress(struct bch_fs *c, struct bio *src,
|
||||
|
||||
if (crc.uncompressed_size << 9 > c->opts.encoded_extent_max ||
|
||||
crc.compressed_size << 9 > c->opts.encoded_extent_max)
|
||||
return -EIO;
|
||||
return -BCH_ERR_decompress_exceeded_max_encoded_extent;
|
||||
|
||||
dst_data = dst_len == dst_iter.bi_size
|
||||
? __bio_map_or_bounce(c, dst, dst_iter, WRITE)
|
||||
|
@ -22,6 +22,13 @@
|
||||
|
||||
#include <linux/ioprio.h>
|
||||
|
||||
static const char * const bch2_data_update_type_strs[] = {
|
||||
#define x(t, n, ...) [n] = #t,
|
||||
BCH_DATA_UPDATE_TYPES()
|
||||
#undef x
|
||||
NULL
|
||||
};
|
||||
|
||||
static void bkey_put_dev_refs(struct bch_fs *c, struct bkey_s_c k)
|
||||
{
|
||||
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
|
||||
@ -181,6 +188,7 @@ static int __bch2_data_update_index_update(struct btree_trans *trans,
|
||||
container_of(op, struct data_update, op);
|
||||
struct keylist *keys = &op->insert_keys;
|
||||
struct bkey_buf _new, _insert;
|
||||
struct printbuf journal_msg = PRINTBUF;
|
||||
int ret = 0;
|
||||
|
||||
bch2_bkey_buf_init(&_new);
|
||||
@ -354,7 +362,7 @@ restart_drop_extra_replicas:
|
||||
printbuf_exit(&buf);
|
||||
|
||||
bch2_fatal_error(c);
|
||||
ret = -EIO;
|
||||
ret = -BCH_ERR_invalid_bkey;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -372,7 +380,12 @@ restart_drop_extra_replicas:
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
|
||||
ret = bch2_insert_snapshot_whiteouts(trans, m->btree_id,
|
||||
printbuf_reset(&journal_msg);
|
||||
prt_str(&journal_msg, bch2_data_update_type_strs[m->type]);
|
||||
|
||||
ret = bch2_trans_log_msg(trans, &journal_msg) ?:
|
||||
bch2_trans_log_bkey(trans, m->btree_id, 0, m->k.k) ?:
|
||||
bch2_insert_snapshot_whiteouts(trans, m->btree_id,
|
||||
k.k->p, bkey_start_pos(&insert->k)) ?:
|
||||
bch2_insert_snapshot_whiteouts(trans, m->btree_id,
|
||||
k.k->p, insert->k.p) ?:
|
||||
@ -417,6 +430,7 @@ nowork:
|
||||
goto next;
|
||||
}
|
||||
out:
|
||||
printbuf_exit(&journal_msg);
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
bch2_bkey_buf_exit(&_insert, c);
|
||||
bch2_bkey_buf_exit(&_new, c);
|
||||
@ -577,6 +591,9 @@ void bch2_data_update_opts_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
|
||||
void bch2_data_update_to_text(struct printbuf *out, struct data_update *m)
|
||||
{
|
||||
prt_str(out, bch2_data_update_type_strs[m->type]);
|
||||
prt_newline(out);
|
||||
|
||||
bch2_data_update_opts_to_text(out, m->op.c, &m->op.opts, &m->data_opts);
|
||||
prt_newline(out);
|
||||
|
||||
@ -738,6 +755,9 @@ int bch2_data_update_init(struct btree_trans *trans,
|
||||
|
||||
bch2_bkey_buf_init(&m->k);
|
||||
bch2_bkey_buf_reassemble(&m->k, c, k);
|
||||
m->type = data_opts.btree_insert_flags & BCH_WATERMARK_copygc
|
||||
? BCH_DATA_UPDATE_copygc
|
||||
: BCH_DATA_UPDATE_rebalance;
|
||||
m->btree_id = btree_id;
|
||||
m->data_opts = data_opts;
|
||||
m->ctxt = ctxt;
|
||||
|
@ -24,7 +24,19 @@ struct data_update_opts {
|
||||
void bch2_data_update_opts_to_text(struct printbuf *, struct bch_fs *,
|
||||
struct bch_io_opts *, struct data_update_opts *);
|
||||
|
||||
#define BCH_DATA_UPDATE_TYPES() \
|
||||
x(copygc, 0) \
|
||||
x(rebalance, 1) \
|
||||
x(promote, 2)
|
||||
|
||||
enum bch_data_update_types {
|
||||
#define x(n, id) BCH_DATA_UPDATE_##n = id,
|
||||
BCH_DATA_UPDATE_TYPES()
|
||||
#undef x
|
||||
};
|
||||
|
||||
struct data_update {
|
||||
enum bch_data_update_types type;
|
||||
/* extent being updated: */
|
||||
bool read_done;
|
||||
enum btree_id btree_id;
|
||||
|
@ -729,3 +729,54 @@ int bch2_readdir(struct bch_fs *c, subvol_inum inum, struct dir_context *ctx)
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
/* fsck */
|
||||
|
||||
static int lookup_first_inode(struct btree_trans *trans, u64 inode_nr,
|
||||
struct bch_inode_unpacked *inode)
|
||||
{
|
||||
struct btree_iter iter;
|
||||
struct bkey_s_c k;
|
||||
int ret;
|
||||
|
||||
for_each_btree_key_norestart(trans, iter, BTREE_ID_inodes, POS(0, inode_nr),
|
||||
BTREE_ITER_all_snapshots, k, ret) {
|
||||
if (k.k->p.offset != inode_nr)
|
||||
break;
|
||||
if (!bkey_is_inode(k.k))
|
||||
continue;
|
||||
ret = bch2_inode_unpack(k, inode);
|
||||
goto found;
|
||||
}
|
||||
ret = -BCH_ERR_ENOENT_inode;
|
||||
found:
|
||||
bch_err_msg(trans->c, ret, "fetching inode %llu", inode_nr);
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bch2_fsck_remove_dirent(struct btree_trans *trans, struct bpos pos)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct btree_iter iter;
|
||||
struct bch_inode_unpacked dir_inode;
|
||||
struct bch_hash_info dir_hash_info;
|
||||
int ret;
|
||||
|
||||
ret = lookup_first_inode(trans, pos.inode, &dir_inode);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dir_hash_info = bch2_hash_info_init(c, &dir_inode);
|
||||
|
||||
bch2_trans_iter_init(trans, &iter, BTREE_ID_dirents, pos, BTREE_ITER_intent);
|
||||
|
||||
ret = bch2_btree_iter_traverse(&iter) ?:
|
||||
bch2_hash_delete_at(trans, bch2_dirent_hash_desc,
|
||||
&dir_hash_info, &iter,
|
||||
BTREE_UPDATE_internal_snapshot_node);
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
err:
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
}
|
||||
|
@ -82,4 +82,6 @@ int bch2_empty_dir_snapshot(struct btree_trans *, u64, u32, u32);
|
||||
int bch2_empty_dir_trans(struct btree_trans *, subvol_inum);
|
||||
int bch2_readdir(struct bch_fs *, subvol_inum, struct dir_context *);
|
||||
|
||||
int bch2_fsck_remove_dirent(struct btree_trans *, struct bpos);
|
||||
|
||||
#endif /* _BCACHEFS_DIRENT_H */
|
||||
|
@ -114,10 +114,9 @@ int bch2_mod_dev_cached_sectors(struct btree_trans *trans,
|
||||
unsigned dev, s64 sectors,
|
||||
bool gc)
|
||||
{
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_replicas,
|
||||
};
|
||||
|
||||
struct disk_accounting_pos acc;
|
||||
memset(&acc, 0, sizeof(acc));
|
||||
acc.type = BCH_DISK_ACCOUNTING_replicas;
|
||||
bch2_replicas_entry_cached(&acc.replicas, dev);
|
||||
|
||||
return bch2_disk_accounting_mod(trans, &acc, §ors, 1, gc);
|
||||
@ -135,6 +134,12 @@ static inline bool is_zero(char *start, char *end)
|
||||
|
||||
#define field_end(p, member) (((void *) (&p.member)) + sizeof(p.member))
|
||||
|
||||
static const unsigned bch2_accounting_type_nr_counters[] = {
|
||||
#define x(f, id, nr) [BCH_DISK_ACCOUNTING_##f] = nr,
|
||||
BCH_DISK_ACCOUNTING_TYPES()
|
||||
#undef x
|
||||
};
|
||||
|
||||
int bch2_accounting_validate(struct bch_fs *c, struct bkey_s_c k,
|
||||
struct bkey_validate_context from)
|
||||
{
|
||||
@ -193,6 +198,11 @@ int bch2_accounting_validate(struct bch_fs *c, struct bkey_s_c k,
|
||||
bkey_fsck_err_on(!is_zero(end, (void *) (&acc_k + 1)),
|
||||
c, accounting_key_junk_at_end,
|
||||
"junk at end of accounting key");
|
||||
|
||||
bkey_fsck_err_on(bch2_accounting_counters(k.k) != bch2_accounting_type_nr_counters[acc_k.type],
|
||||
c, accounting_key_nr_counters_wrong,
|
||||
"accounting key with %u counters, should be %u",
|
||||
bch2_accounting_counters(k.k), bch2_accounting_type_nr_counters[acc_k.type]);
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
@ -635,7 +645,7 @@ static int bch2_disk_accounting_validate_late(struct btree_trans *trans,
|
||||
|
||||
if (fsck_err_on(!bch2_replicas_marked_locked(c, &r.e),
|
||||
trans, accounting_replicas_not_marked,
|
||||
"accounting not marked in superblock replicas\n %s",
|
||||
"accounting not marked in superblock replicas\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_accounting_key_to_text(&buf, &acc),
|
||||
buf.buf))) {
|
||||
@ -665,7 +675,7 @@ fsck_err:
|
||||
return ret;
|
||||
invalid_device:
|
||||
if (fsck_err(trans, accounting_to_invalid_device,
|
||||
"accounting entry points to invalid device %i\n %s",
|
||||
"accounting entry points to invalid device %i\n%s",
|
||||
invalid_dev,
|
||||
(printbuf_reset(&buf),
|
||||
bch2_accounting_key_to_text(&buf, &acc),
|
||||
@ -726,7 +736,9 @@ int bch2_accounting_read(struct bch_fs *c)
|
||||
break;
|
||||
|
||||
if (!bch2_accounting_is_mem(acc_k)) {
|
||||
struct disk_accounting_pos next = { .type = acc_k.type + 1 };
|
||||
struct disk_accounting_pos next;
|
||||
memset(&next, 0, sizeof(next));
|
||||
next.type = acc_k.type + 1;
|
||||
bch2_btree_iter_set_pos(&iter, disk_accounting_pos_to_bpos(&next));
|
||||
continue;
|
||||
}
|
||||
@ -882,15 +894,13 @@ int bch2_dev_usage_remove(struct bch_fs *c, unsigned dev)
|
||||
int bch2_dev_usage_init(struct bch_dev *ca, bool gc)
|
||||
{
|
||||
struct bch_fs *c = ca->fs;
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_dev_data_type,
|
||||
.dev_data_type.dev = ca->dev_idx,
|
||||
.dev_data_type.data_type = BCH_DATA_free,
|
||||
};
|
||||
u64 v[3] = { ca->mi.nbuckets - ca->mi.first_bucket, 0, 0 };
|
||||
|
||||
int ret = bch2_trans_do(c, ({
|
||||
bch2_disk_accounting_mod(trans, &acc, v, ARRAY_SIZE(v), gc) ?:
|
||||
bch2_disk_accounting_mod2(trans, gc,
|
||||
v, dev_data_type,
|
||||
.dev = ca->dev_idx,
|
||||
.data_type = BCH_DATA_free) ?:
|
||||
(!gc ? bch2_trans_commit(trans, NULL, NULL, 0) : 0);
|
||||
}));
|
||||
bch_err_fn(c, ret);
|
||||
@ -917,7 +927,9 @@ void bch2_verify_accounting_clean(struct bch_fs *c)
|
||||
break;
|
||||
|
||||
if (!bch2_accounting_is_mem(acc_k)) {
|
||||
struct disk_accounting_pos next = { .type = acc_k.type + 1 };
|
||||
struct disk_accounting_pos next;
|
||||
memset(&next, 0, sizeof(next));
|
||||
next.type = acc_k.type + 1;
|
||||
bch2_btree_iter_set_pos(&iter, disk_accounting_pos_to_bpos(&next));
|
||||
continue;
|
||||
}
|
||||
|
@ -33,10 +33,12 @@ static inline bool bch2_accounting_key_is_zero(struct bkey_s_c_accounting a)
|
||||
static inline void bch2_accounting_accumulate(struct bkey_i_accounting *dst,
|
||||
struct bkey_s_c_accounting src)
|
||||
{
|
||||
EBUG_ON(dst->k.u64s != src.k->u64s);
|
||||
|
||||
for (unsigned i = 0; i < bch2_accounting_counters(&dst->k); i++)
|
||||
for (unsigned i = 0;
|
||||
i < min(bch2_accounting_counters(&dst->k),
|
||||
bch2_accounting_counters(src.k));
|
||||
i++)
|
||||
dst->v.d[i] += src.v->d[i];
|
||||
|
||||
if (bversion_cmp(dst->k.bversion, src.k->bversion) < 0)
|
||||
dst->k.bversion = src.k->bversion;
|
||||
}
|
||||
@ -85,6 +87,24 @@ static inline struct bpos disk_accounting_pos_to_bpos(struct disk_accounting_pos
|
||||
|
||||
int bch2_disk_accounting_mod(struct btree_trans *, struct disk_accounting_pos *,
|
||||
s64 *, unsigned, bool);
|
||||
|
||||
#define disk_accounting_key_init(_k, _type, ...) \
|
||||
do { \
|
||||
memset(&(_k), 0, sizeof(_k)); \
|
||||
(_k).type = BCH_DISK_ACCOUNTING_##_type; \
|
||||
(_k)._type = (struct bch_acct_##_type) { __VA_ARGS__ }; \
|
||||
} while (0)
|
||||
|
||||
#define bch2_disk_accounting_mod2_nr(_trans, _gc, _v, _nr, ...) \
|
||||
({ \
|
||||
struct disk_accounting_pos pos; \
|
||||
disk_accounting_key_init(pos, __VA_ARGS__); \
|
||||
bch2_disk_accounting_mod(trans, &pos, _v, _nr, _gc); \
|
||||
})
|
||||
|
||||
#define bch2_disk_accounting_mod2(_trans, _gc, _v, ...) \
|
||||
bch2_disk_accounting_mod2_nr(_trans, _gc, _v, ARRAY_SIZE(_v), __VA_ARGS__)
|
||||
|
||||
int bch2_mod_dev_cached_sectors(struct btree_trans *, unsigned, s64, bool);
|
||||
|
||||
int bch2_accounting_validate(struct bch_fs *, struct bkey_s_c,
|
||||
|
@ -95,40 +95,81 @@ static inline bool data_type_is_hidden(enum bch_data_type type)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* field 1: name
|
||||
* field 2: id
|
||||
* field 3: number of counters (max 3)
|
||||
*/
|
||||
|
||||
#define BCH_DISK_ACCOUNTING_TYPES() \
|
||||
x(nr_inodes, 0) \
|
||||
x(persistent_reserved, 1) \
|
||||
x(replicas, 2) \
|
||||
x(dev_data_type, 3) \
|
||||
x(compression, 4) \
|
||||
x(snapshot, 5) \
|
||||
x(btree, 6) \
|
||||
x(rebalance_work, 7) \
|
||||
x(inum, 8)
|
||||
x(nr_inodes, 0, 1) \
|
||||
x(persistent_reserved, 1, 1) \
|
||||
x(replicas, 2, 1) \
|
||||
x(dev_data_type, 3, 3) \
|
||||
x(compression, 4, 3) \
|
||||
x(snapshot, 5, 1) \
|
||||
x(btree, 6, 1) \
|
||||
x(rebalance_work, 7, 1) \
|
||||
x(inum, 8, 3)
|
||||
|
||||
enum disk_accounting_type {
|
||||
#define x(f, nr) BCH_DISK_ACCOUNTING_##f = nr,
|
||||
#define x(f, nr, ...) BCH_DISK_ACCOUNTING_##f = nr,
|
||||
BCH_DISK_ACCOUNTING_TYPES()
|
||||
#undef x
|
||||
BCH_DISK_ACCOUNTING_TYPE_NR,
|
||||
};
|
||||
|
||||
struct bch_nr_inodes {
|
||||
/*
|
||||
* No subtypes - number of inodes in the entire filesystem
|
||||
*
|
||||
* XXX: perhaps we could add a per-subvolume counter?
|
||||
*/
|
||||
struct bch_acct_nr_inodes {
|
||||
};
|
||||
|
||||
struct bch_persistent_reserved {
|
||||
/*
|
||||
* Tracks KEY_TYPE_reservation sectors, broken out by number of replicas for the
|
||||
* reservation:
|
||||
*/
|
||||
struct bch_acct_persistent_reserved {
|
||||
__u8 nr_replicas;
|
||||
};
|
||||
|
||||
struct bch_dev_data_type {
|
||||
/*
|
||||
* device, data type counter fields:
|
||||
* [
|
||||
* nr_buckets
|
||||
* live sectors (in buckets of that data type)
|
||||
* sectors of internal fragmentation
|
||||
* ]
|
||||
*
|
||||
* XXX: live sectors should've been done differently, you can have multiple data
|
||||
* types in the same bucket (user, stripe, cached) and this collapses them to
|
||||
* the bucket data type, and makes the internal fragmentation counter redundant
|
||||
*/
|
||||
struct bch_acct_dev_data_type {
|
||||
__u8 dev;
|
||||
__u8 data_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* Compression type fields:
|
||||
* [
|
||||
* number of extents
|
||||
* uncompressed size
|
||||
* compressed size
|
||||
* ]
|
||||
*
|
||||
* Compression ratio, average extent size (fragmentation).
|
||||
*/
|
||||
struct bch_acct_compression {
|
||||
__u8 type;
|
||||
};
|
||||
|
||||
/*
|
||||
* On disk usage by snapshot id; counts same values as replicas counter, but
|
||||
* aggregated differently
|
||||
*/
|
||||
struct bch_acct_snapshot {
|
||||
__u32 id;
|
||||
} __packed;
|
||||
@ -137,10 +178,27 @@ struct bch_acct_btree {
|
||||
__u32 id;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* inum counter fields:
|
||||
* [
|
||||
* number of extents
|
||||
* sum of extent sizes - bkey size
|
||||
* this field is similar to inode.bi_sectors, except here extents in
|
||||
* different snapshots but the same inode number are all collapsed to the
|
||||
* same counter
|
||||
* sum of on disk size - same values tracked by replicas counters
|
||||
* ]
|
||||
*
|
||||
* This tracks on disk fragmentation.
|
||||
*/
|
||||
struct bch_acct_inum {
|
||||
__u64 inum;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Simple counter of the amount of data (on disk sectors) rebalance needs to
|
||||
* move, extents counted here are also in the rebalance_work btree.
|
||||
*/
|
||||
struct bch_acct_rebalance_work {
|
||||
};
|
||||
|
||||
@ -149,10 +207,10 @@ struct disk_accounting_pos {
|
||||
struct {
|
||||
__u8 type;
|
||||
union {
|
||||
struct bch_nr_inodes nr_inodes;
|
||||
struct bch_persistent_reserved persistent_reserved;
|
||||
struct bch_acct_nr_inodes nr_inodes;
|
||||
struct bch_acct_persistent_reserved persistent_reserved;
|
||||
struct bch_replicas_entry_v1 replicas;
|
||||
struct bch_dev_data_type dev_data_type;
|
||||
struct bch_acct_dev_data_type dev_data_type;
|
||||
struct bch_acct_compression compression;
|
||||
struct bch_acct_snapshot snapshot;
|
||||
struct bch_acct_btree btree;
|
||||
|
@ -320,7 +320,7 @@ static int mark_stripe_bucket(struct btree_trans *trans,
|
||||
|
||||
if (flags & BTREE_TRIGGER_gc) {
|
||||
struct bucket *g = gc_bucket(ca, bucket.offset);
|
||||
if (bch2_fs_inconsistent_on(!g, c, "reference to invalid bucket on device %u\n %s",
|
||||
if (bch2_fs_inconsistent_on(!g, c, "reference to invalid bucket on device %u\n%s",
|
||||
ptr->dev,
|
||||
(bch2_bkey_val_to_text(&buf, c, s.s_c), buf.buf))) {
|
||||
ret = -BCH_ERR_mark_stripe;
|
||||
@ -453,9 +453,9 @@ int bch2_trigger_stripe(struct btree_trans *trans,
|
||||
if (new_s) {
|
||||
s64 sectors = (u64) le16_to_cpu(new_s->sectors) * new_s->nr_redundant;
|
||||
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_replicas,
|
||||
};
|
||||
struct disk_accounting_pos acc;
|
||||
memset(&acc, 0, sizeof(acc));
|
||||
acc.type = BCH_DISK_ACCOUNTING_replicas;
|
||||
bch2_bkey_to_replicas(&acc.replicas, new);
|
||||
int ret = bch2_disk_accounting_mod(trans, &acc, §ors, 1, gc);
|
||||
if (ret)
|
||||
@ -468,9 +468,9 @@ int bch2_trigger_stripe(struct btree_trans *trans,
|
||||
if (old_s) {
|
||||
s64 sectors = -((s64) le16_to_cpu(old_s->sectors)) * old_s->nr_redundant;
|
||||
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_replicas,
|
||||
};
|
||||
struct disk_accounting_pos acc;
|
||||
memset(&acc, 0, sizeof(acc));
|
||||
acc.type = BCH_DISK_ACCOUNTING_replicas;
|
||||
bch2_bkey_to_replicas(&acc.replicas, old);
|
||||
int ret = bch2_disk_accounting_mod(trans, &acc, §ors, 1, gc);
|
||||
if (ret)
|
||||
@ -1124,7 +1124,7 @@ static int ec_stripe_update_extent(struct btree_trans *trans,
|
||||
|
||||
bch2_fs_inconsistent(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
return -EIO;
|
||||
return -BCH_ERR_erasure_coding_found_btree_node;
|
||||
}
|
||||
|
||||
k = bch2_backpointer_get_key(trans, bp, &iter, BTREE_ITER_intent, last_flushed);
|
||||
@ -1190,7 +1190,7 @@ static int ec_stripe_update_bucket(struct btree_trans *trans, struct ec_stripe_b
|
||||
|
||||
struct bch_dev *ca = bch2_dev_tryget(c, ptr.dev);
|
||||
if (!ca)
|
||||
return -EIO;
|
||||
return -BCH_ERR_ENOENT_dev_not_found;
|
||||
|
||||
struct bpos bucket_pos = PTR_BUCKET_POS(ca, &ptr);
|
||||
|
||||
@ -1227,21 +1227,19 @@ static int ec_stripe_update_extents(struct bch_fs *c, struct ec_stripe_buf *s)
|
||||
{
|
||||
struct btree_trans *trans = bch2_trans_get(c);
|
||||
struct bch_stripe *v = &bkey_i_to_stripe(&s->key)->v;
|
||||
unsigned i, nr_data = v->nr_blocks - v->nr_redundant;
|
||||
int ret = 0;
|
||||
unsigned nr_data = v->nr_blocks - v->nr_redundant;
|
||||
|
||||
ret = bch2_btree_write_buffer_flush_sync(trans);
|
||||
int ret = bch2_btree_write_buffer_flush_sync(trans);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < nr_data; i++) {
|
||||
for (unsigned i = 0; i < nr_data; i++) {
|
||||
ret = ec_stripe_update_bucket(trans, s, i);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
err:
|
||||
bch2_trans_put(trans);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1451,11 +1449,11 @@ static void ec_stripe_new_cancel(struct bch_fs *c, struct ec_stripe_head *h, int
|
||||
ec_stripe_new_set_pending(c, h);
|
||||
}
|
||||
|
||||
void bch2_ec_bucket_cancel(struct bch_fs *c, struct open_bucket *ob)
|
||||
void bch2_ec_bucket_cancel(struct bch_fs *c, struct open_bucket *ob, int err)
|
||||
{
|
||||
struct ec_stripe_new *s = ob->ec;
|
||||
|
||||
s->err = -EIO;
|
||||
s->err = err;
|
||||
}
|
||||
|
||||
void *bch2_writepoint_ec_buf(struct bch_fs *c, struct write_point *wp)
|
||||
@ -2112,14 +2110,14 @@ static int bch2_invalidate_stripe_to_dev(struct btree_trans *trans, struct bkey_
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_replicas,
|
||||
};
|
||||
struct disk_accounting_pos acc;
|
||||
|
||||
s64 sectors = 0;
|
||||
for (unsigned i = 0; i < s->v.nr_blocks; i++)
|
||||
sectors -= stripe_blockcount_get(&s->v, i);
|
||||
|
||||
memset(&acc, 0, sizeof(acc));
|
||||
acc.type = BCH_DISK_ACCOUNTING_replicas;
|
||||
bch2_bkey_to_replicas(&acc.replicas, bkey_i_to_s_c(&s->k_i));
|
||||
acc.replicas.data_type = BCH_DATA_user;
|
||||
ret = bch2_disk_accounting_mod(trans, &acc, §ors, 1, false);
|
||||
@ -2133,6 +2131,8 @@ static int bch2_invalidate_stripe_to_dev(struct btree_trans *trans, struct bkey_
|
||||
|
||||
sectors = -sectors;
|
||||
|
||||
memset(&acc, 0, sizeof(acc));
|
||||
acc.type = BCH_DISK_ACCOUNTING_replicas;
|
||||
bch2_bkey_to_replicas(&acc.replicas, bkey_i_to_s_c(&s->k_i));
|
||||
acc.replicas.data_type = BCH_DATA_user;
|
||||
ret = bch2_disk_accounting_mod(trans, &acc, §ors, 1, false);
|
||||
|
@ -249,7 +249,7 @@ int bch2_ec_read_extent(struct btree_trans *, struct bch_read_bio *, struct bkey
|
||||
|
||||
void *bch2_writepoint_ec_buf(struct bch_fs *, struct write_point *);
|
||||
|
||||
void bch2_ec_bucket_cancel(struct bch_fs *, struct open_bucket *);
|
||||
void bch2_ec_bucket_cancel(struct bch_fs *, struct open_bucket *, int);
|
||||
|
||||
int bch2_ec_stripe_new_alloc(struct bch_fs *, struct ec_stripe_head *);
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
#define BCH_ERRCODES() \
|
||||
x(ERANGE, ERANGE_option_too_small) \
|
||||
x(ERANGE, ERANGE_option_too_big) \
|
||||
x(EINVAL, injected) \
|
||||
x(BCH_ERR_injected, injected_fs_start) \
|
||||
x(EINVAL, mount_option) \
|
||||
x(BCH_ERR_mount_option, option_name) \
|
||||
x(BCH_ERR_mount_option, option_value) \
|
||||
@ -116,6 +118,7 @@
|
||||
x(ENOENT, ENOENT_snapshot_tree) \
|
||||
x(ENOENT, ENOENT_dirent_doesnt_match_inode) \
|
||||
x(ENOENT, ENOENT_dev_not_found) \
|
||||
x(ENOENT, ENOENT_dev_bucket_not_found) \
|
||||
x(ENOENT, ENOENT_dev_idx_not_found) \
|
||||
x(ENOENT, ENOENT_inode_no_backpointer) \
|
||||
x(ENOENT, ENOENT_no_snapshot_tree_subvol) \
|
||||
@ -207,6 +210,7 @@
|
||||
x(EINVAL, no_resize_with_buckets_nouse) \
|
||||
x(EINVAL, inode_unpack_error) \
|
||||
x(EINVAL, varint_decode_error) \
|
||||
x(EINVAL, erasure_coding_found_btree_node) \
|
||||
x(EOPNOTSUPP, may_not_use_incompat_feature) \
|
||||
x(EROFS, erofs_trans_commit) \
|
||||
x(EROFS, erofs_no_writes) \
|
||||
@ -267,6 +271,7 @@
|
||||
x(BCH_ERR_operation_blocked, nocow_lock_blocked) \
|
||||
x(EIO, journal_shutdown) \
|
||||
x(EIO, journal_flush_err) \
|
||||
x(EIO, journal_write_err) \
|
||||
x(EIO, btree_node_read_err) \
|
||||
x(BCH_ERR_btree_node_read_err, btree_node_read_err_cached) \
|
||||
x(EIO, sb_not_downgraded) \
|
||||
@ -275,6 +280,7 @@
|
||||
x(EIO, btree_node_read_validate_error) \
|
||||
x(EIO, btree_need_topology_repair) \
|
||||
x(EIO, bucket_ref_update) \
|
||||
x(EIO, trigger_alloc) \
|
||||
x(EIO, trigger_pointer) \
|
||||
x(EIO, trigger_stripe_pointer) \
|
||||
x(EIO, metadata_bucket_inconsistency) \
|
||||
@ -290,8 +296,21 @@
|
||||
x(EIO, EIO_fault_injected) \
|
||||
x(EIO, ec_block_read) \
|
||||
x(EIO, ec_block_write) \
|
||||
x(EIO, data_read) \
|
||||
x(EIO, recompute_checksum) \
|
||||
x(EIO, decompress) \
|
||||
x(BCH_ERR_decompress, decompress_exceeded_max_encoded_extent) \
|
||||
x(BCH_ERR_decompress, decompress_lz4) \
|
||||
x(BCH_ERR_decompress, decompress_gzip) \
|
||||
x(BCH_ERR_decompress, decompress_zstd_src_len_bad) \
|
||||
x(BCH_ERR_decompress, decompress_zstd) \
|
||||
x(EIO, data_write) \
|
||||
x(BCH_ERR_data_write, data_write_io) \
|
||||
x(BCH_ERR_data_write, data_write_csum) \
|
||||
x(BCH_ERR_data_write, data_write_invalid_ptr) \
|
||||
x(BCH_ERR_data_write, data_write_misaligned) \
|
||||
x(BCH_ERR_decompress, data_read) \
|
||||
x(BCH_ERR_data_read, no_device_to_read_from) \
|
||||
x(BCH_ERR_data_read, no_devices_valid) \
|
||||
x(BCH_ERR_data_read, data_read_io_err) \
|
||||
x(BCH_ERR_data_read, data_read_csum_err) \
|
||||
x(BCH_ERR_data_read, data_read_retry) \
|
||||
|
@ -3,15 +3,24 @@
|
||||
#include "btree_cache.h"
|
||||
#include "btree_iter.h"
|
||||
#include "error.h"
|
||||
#include "fs-common.h"
|
||||
#include "journal.h"
|
||||
#include "namei.h"
|
||||
#include "recovery_passes.h"
|
||||
#include "super.h"
|
||||
#include "thread_with_file.h"
|
||||
|
||||
#define FSCK_ERR_RATELIMIT_NR 10
|
||||
|
||||
bool bch2_inconsistent_error(struct bch_fs *c)
|
||||
void bch2_log_msg_start(struct bch_fs *c, struct printbuf *out)
|
||||
{
|
||||
printbuf_indent_add_nextline(out, 2);
|
||||
|
||||
#ifdef BCACHEFS_LOG_PREFIX
|
||||
prt_printf(out, bch2_log_msg(c, ""));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool __bch2_inconsistent_error(struct bch_fs *c, struct printbuf *out)
|
||||
{
|
||||
set_bit(BCH_FS_error, &c->flags);
|
||||
|
||||
@ -21,10 +30,11 @@ bool bch2_inconsistent_error(struct bch_fs *c)
|
||||
case BCH_ON_ERROR_fix_safe:
|
||||
case BCH_ON_ERROR_ro:
|
||||
if (bch2_fs_emergency_read_only(c))
|
||||
bch_err(c, "inconsistency detected - emergency read only at journal seq %llu",
|
||||
journal_cur_seq(&c->journal));
|
||||
prt_printf(out, "inconsistency detected - emergency read only at journal seq %llu\n",
|
||||
journal_cur_seq(&c->journal));
|
||||
return true;
|
||||
case BCH_ON_ERROR_panic:
|
||||
bch2_print_string_as_lines(KERN_ERR, out->buf);
|
||||
panic(bch2_fmt(c, "panic after error"));
|
||||
return true;
|
||||
default:
|
||||
@ -32,11 +42,63 @@ bool bch2_inconsistent_error(struct bch_fs *c)
|
||||
}
|
||||
}
|
||||
|
||||
int bch2_topology_error(struct bch_fs *c)
|
||||
bool bch2_inconsistent_error(struct bch_fs *c)
|
||||
{
|
||||
struct printbuf buf = PRINTBUF;
|
||||
printbuf_indent_add_nextline(&buf, 2);
|
||||
|
||||
bool ret = __bch2_inconsistent_error(c, &buf);
|
||||
if (ret)
|
||||
bch_err(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
__printf(3, 0)
|
||||
static bool bch2_fs_trans_inconsistent(struct bch_fs *c, struct btree_trans *trans,
|
||||
const char *fmt, va_list args)
|
||||
{
|
||||
struct printbuf buf = PRINTBUF;
|
||||
|
||||
bch2_log_msg_start(c, &buf);
|
||||
|
||||
prt_vprintf(&buf, fmt, args);
|
||||
prt_newline(&buf);
|
||||
|
||||
if (trans)
|
||||
bch2_trans_updates_to_text(&buf, trans);
|
||||
bool ret = __bch2_inconsistent_error(c, &buf);
|
||||
bch2_print_string_as_lines(KERN_ERR, buf.buf);
|
||||
|
||||
printbuf_exit(&buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool bch2_fs_inconsistent(struct bch_fs *c, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool ret = bch2_fs_trans_inconsistent(c, NULL, fmt, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool bch2_trans_inconsistent(struct btree_trans *trans, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool ret = bch2_fs_trans_inconsistent(trans->c, trans, fmt, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __bch2_topology_error(struct bch_fs *c, struct printbuf *out)
|
||||
{
|
||||
prt_printf(out, "btree topology error: ");
|
||||
|
||||
set_bit(BCH_FS_topology_error, &c->flags);
|
||||
if (!test_bit(BCH_FS_recovery_running, &c->flags)) {
|
||||
bch2_inconsistent_error(c);
|
||||
__bch2_inconsistent_error(c, out);
|
||||
return -BCH_ERR_btree_need_topology_repair;
|
||||
} else {
|
||||
return bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_check_topology) ?:
|
||||
@ -44,6 +106,24 @@ int bch2_topology_error(struct bch_fs *c)
|
||||
}
|
||||
}
|
||||
|
||||
int bch2_fs_topology_error(struct bch_fs *c, const char *fmt, ...)
|
||||
{
|
||||
struct printbuf buf = PRINTBUF;
|
||||
|
||||
bch2_log_msg_start(c, &buf);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
prt_vprintf(&buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
int ret = __bch2_topology_error(c, &buf);
|
||||
bch2_print_string_as_lines(KERN_ERR, buf.buf);
|
||||
|
||||
printbuf_exit(&buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_fatal_error(struct bch_fs *c)
|
||||
{
|
||||
if (bch2_fs_emergency_read_only(c))
|
||||
@ -184,7 +264,8 @@ static enum ask_yn bch2_fsck_ask_yn(struct bch_fs *c, struct btree_trans *trans)
|
||||
|
||||
#endif
|
||||
|
||||
static struct fsck_err_state *fsck_err_get(struct bch_fs *c, const char *fmt)
|
||||
static struct fsck_err_state *fsck_err_get(struct bch_fs *c,
|
||||
enum bch_sb_error_id id)
|
||||
{
|
||||
struct fsck_err_state *s;
|
||||
|
||||
@ -192,7 +273,7 @@ static struct fsck_err_state *fsck_err_get(struct bch_fs *c, const char *fmt)
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(s, &c->fsck_error_msgs, list)
|
||||
if (s->fmt == fmt) {
|
||||
if (s->id == id) {
|
||||
/*
|
||||
* move it to the head of the list: repeated fsck errors
|
||||
* are common
|
||||
@ -210,7 +291,7 @@ static struct fsck_err_state *fsck_err_get(struct bch_fs *c, const char *fmt)
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&s->list);
|
||||
s->fmt = fmt;
|
||||
s->id = id;
|
||||
list_add(&s->list, &c->fsck_error_msgs);
|
||||
return s;
|
||||
}
|
||||
@ -260,15 +341,59 @@ static int do_fsck_ask_yn(struct bch_fs *c,
|
||||
return ask;
|
||||
}
|
||||
|
||||
static struct fsck_err_state *count_fsck_err_locked(struct bch_fs *c,
|
||||
enum bch_sb_error_id id, const char *msg,
|
||||
bool *repeat, bool *print, bool *suppress)
|
||||
{
|
||||
bch2_sb_error_count(c, id);
|
||||
|
||||
struct fsck_err_state *s = fsck_err_get(c, id);
|
||||
if (s) {
|
||||
/*
|
||||
* We may be called multiple times for the same error on
|
||||
* transaction restart - this memoizes instead of asking the user
|
||||
* multiple times for the same error:
|
||||
*/
|
||||
if (s->last_msg && !strcmp(msg, s->last_msg)) {
|
||||
*repeat = true;
|
||||
*print = false;
|
||||
return s;
|
||||
}
|
||||
|
||||
kfree(s->last_msg);
|
||||
s->last_msg = kstrdup(msg, GFP_KERNEL);
|
||||
|
||||
if (c->opts.ratelimit_errors &&
|
||||
s->nr >= FSCK_ERR_RATELIMIT_NR) {
|
||||
if (s->nr == FSCK_ERR_RATELIMIT_NR)
|
||||
*suppress = true;
|
||||
else
|
||||
*print = false;
|
||||
}
|
||||
|
||||
s->nr++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void __bch2_count_fsck_err(struct bch_fs *c,
|
||||
enum bch_sb_error_id id, const char *msg,
|
||||
bool *repeat, bool *print, bool *suppress)
|
||||
{
|
||||
bch2_sb_error_count(c, id);
|
||||
|
||||
mutex_lock(&c->fsck_error_msgs_lock);
|
||||
count_fsck_err_locked(c, id, msg, repeat, print, suppress);
|
||||
mutex_unlock(&c->fsck_error_msgs_lock);
|
||||
}
|
||||
|
||||
int __bch2_fsck_err(struct bch_fs *c,
|
||||
struct btree_trans *trans,
|
||||
enum bch_fsck_flags flags,
|
||||
enum bch_sb_error_id err,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
struct fsck_err_state *s = NULL;
|
||||
va_list args;
|
||||
bool print = true, suppressing = false, inconsistent = false, exiting = false;
|
||||
struct printbuf buf = PRINTBUF, *out = &buf;
|
||||
int ret = -BCH_ERR_fsck_ignore;
|
||||
const char *action_orig = "fix?", *action = action_orig;
|
||||
@ -303,7 +428,12 @@ int __bch2_fsck_err(struct bch_fs *c,
|
||||
? -BCH_ERR_fsck_fix
|
||||
: -BCH_ERR_fsck_ignore;
|
||||
|
||||
bch2_sb_error_count(c, err);
|
||||
printbuf_indent_add_nextline(out, 2);
|
||||
|
||||
#ifdef BCACHEFS_LOG_PREFIX
|
||||
if (strncmp(fmt, "bcachefs", 8))
|
||||
prt_printf(out, bch2_log_msg(c, ""));
|
||||
#endif
|
||||
|
||||
va_start(args, fmt);
|
||||
prt_vprintf(out, fmt, args);
|
||||
@ -323,42 +453,15 @@ int __bch2_fsck_err(struct bch_fs *c,
|
||||
}
|
||||
|
||||
mutex_lock(&c->fsck_error_msgs_lock);
|
||||
s = fsck_err_get(c, fmt);
|
||||
if (s) {
|
||||
/*
|
||||
* We may be called multiple times for the same error on
|
||||
* transaction restart - this memoizes instead of asking the user
|
||||
* multiple times for the same error:
|
||||
*/
|
||||
if (s->last_msg && !strcmp(buf.buf, s->last_msg)) {
|
||||
ret = s->ret;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
kfree(s->last_msg);
|
||||
s->last_msg = kstrdup(buf.buf, GFP_KERNEL);
|
||||
if (!s->last_msg) {
|
||||
ret = -ENOMEM;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
if (c->opts.ratelimit_errors &&
|
||||
!(flags & FSCK_NO_RATELIMIT) &&
|
||||
s->nr >= FSCK_ERR_RATELIMIT_NR) {
|
||||
if (s->nr == FSCK_ERR_RATELIMIT_NR)
|
||||
suppressing = true;
|
||||
else
|
||||
print = false;
|
||||
}
|
||||
|
||||
s->nr++;
|
||||
bool repeat = false, print = true, suppress = false;
|
||||
bool inconsistent = false, exiting = false;
|
||||
struct fsck_err_state *s =
|
||||
count_fsck_err_locked(c, err, buf.buf, &repeat, &print, &suppress);
|
||||
if (repeat) {
|
||||
ret = s->ret;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
#ifdef BCACHEFS_LOG_PREFIX
|
||||
if (!strncmp(fmt, "bcachefs:", 9))
|
||||
prt_printf(out, bch2_log_msg(c, ""));
|
||||
#endif
|
||||
|
||||
if ((flags & FSCK_AUTOFIX) &&
|
||||
(c->opts.errors == BCH_ON_ERROR_continue ||
|
||||
c->opts.errors == BCH_ON_ERROR_fix_safe)) {
|
||||
@ -377,6 +480,7 @@ int __bch2_fsck_err(struct bch_fs *c,
|
||||
!(flags & (FSCK_CAN_FIX|FSCK_CAN_IGNORE))) {
|
||||
prt_str(out, ", shutting down");
|
||||
inconsistent = true;
|
||||
print = true;
|
||||
ret = -BCH_ERR_fsck_errors_not_fixed;
|
||||
} else if (flags & FSCK_CAN_FIX) {
|
||||
prt_str(out, ", ");
|
||||
@ -435,24 +539,30 @@ int __bch2_fsck_err(struct bch_fs *c,
|
||||
print = true;
|
||||
}
|
||||
print:
|
||||
prt_newline(out);
|
||||
|
||||
if (inconsistent)
|
||||
__bch2_inconsistent_error(c, out);
|
||||
else if (exiting)
|
||||
prt_printf(out, "Unable to continue, halting\n");
|
||||
else if (suppress)
|
||||
prt_printf(out, "Ratelimiting new instances of previous error\n");
|
||||
|
||||
if (print) {
|
||||
/* possibly strip an empty line, from printbuf_indent_add */
|
||||
while (out->pos && out->buf[out->pos - 1] == ' ')
|
||||
--out->pos;
|
||||
printbuf_nul_terminate(out);
|
||||
|
||||
if (bch2_fs_stdio_redirect(c))
|
||||
bch2_print(c, "%s\n", out->buf);
|
||||
bch2_print(c, "%s", out->buf);
|
||||
else
|
||||
bch2_print_string_as_lines(KERN_ERR, out->buf);
|
||||
}
|
||||
|
||||
if (exiting)
|
||||
bch_err(c, "Unable to continue, halting");
|
||||
else if (suppressing)
|
||||
bch_err(c, "Ratelimiting new instances of previous error");
|
||||
|
||||
if (s)
|
||||
s->ret = ret;
|
||||
|
||||
if (inconsistent)
|
||||
bch2_inconsistent_error(c);
|
||||
|
||||
/*
|
||||
* We don't yet track whether the filesystem currently has errors, for
|
||||
* log_fsck_err()s: that would require us to track for every error type
|
||||
@ -514,16 +624,14 @@ int __bch2_bkey_fsck_err(struct bch_fs *c,
|
||||
prt_printf(&buf, " level=%u: ", from.level);
|
||||
|
||||
bch2_bkey_val_to_text(&buf, c, k);
|
||||
prt_str(&buf, "\n ");
|
||||
prt_newline(&buf);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
prt_vprintf(&buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
prt_str(&buf, ": delete?");
|
||||
|
||||
int ret = __bch2_fsck_err(c, NULL, fsck_flags, err, "%s", buf.buf);
|
||||
int ret = __bch2_fsck_err(c, NULL, fsck_flags, err, "%s, delete?", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
return ret;
|
||||
}
|
||||
@ -536,7 +644,7 @@ void bch2_flush_fsck_errs(struct bch_fs *c)
|
||||
|
||||
list_for_each_entry_safe(s, n, &c->fsck_error_msgs, list) {
|
||||
if (s->ratelimited && s->last_msg)
|
||||
bch_err(c, "Saw %llu errors like:\n %s", s->nr, s->last_msg);
|
||||
bch_err(c, "Saw %llu errors like:\n %s", s->nr, s->last_msg);
|
||||
|
||||
list_del(&s->list);
|
||||
kfree(s->last_msg);
|
||||
|
@ -18,6 +18,8 @@ struct work_struct;
|
||||
|
||||
/* Error messages: */
|
||||
|
||||
void bch2_log_msg_start(struct bch_fs *, struct printbuf *);
|
||||
|
||||
/*
|
||||
* Inconsistency errors: The on disk data is inconsistent. If these occur during
|
||||
* initial recovery, they don't indicate a bug in the running code - we walk all
|
||||
@ -29,21 +31,10 @@ struct work_struct;
|
||||
* BCH_ON_ERROR_CONTINUE mode
|
||||
*/
|
||||
|
||||
bool __bch2_inconsistent_error(struct bch_fs *, struct printbuf *);
|
||||
bool bch2_inconsistent_error(struct bch_fs *);
|
||||
|
||||
int bch2_topology_error(struct bch_fs *);
|
||||
|
||||
#define bch2_fs_topology_error(c, ...) \
|
||||
({ \
|
||||
bch_err(c, "btree topology error: " __VA_ARGS__); \
|
||||
bch2_topology_error(c); \
|
||||
})
|
||||
|
||||
#define bch2_fs_inconsistent(c, ...) \
|
||||
({ \
|
||||
bch_err(c, __VA_ARGS__); \
|
||||
bch2_inconsistent_error(c); \
|
||||
})
|
||||
__printf(2, 3)
|
||||
bool bch2_fs_inconsistent(struct bch_fs *, const char *, ...);
|
||||
|
||||
#define bch2_fs_inconsistent_on(cond, ...) \
|
||||
({ \
|
||||
@ -53,26 +44,21 @@ int bch2_topology_error(struct bch_fs *);
|
||||
_ret; \
|
||||
})
|
||||
|
||||
/*
|
||||
* When a transaction update discovers or is causing a fs inconsistency, it's
|
||||
* helpful to also dump the pending updates:
|
||||
*/
|
||||
#define bch2_trans_inconsistent(trans, ...) \
|
||||
({ \
|
||||
bch_err(trans->c, __VA_ARGS__); \
|
||||
bch2_dump_trans_updates(trans); \
|
||||
bch2_inconsistent_error(trans->c); \
|
||||
})
|
||||
__printf(2, 3)
|
||||
bool bch2_trans_inconsistent(struct btree_trans *, const char *, ...);
|
||||
|
||||
#define bch2_trans_inconsistent_on(cond, trans, ...) \
|
||||
#define bch2_trans_inconsistent_on(cond, ...) \
|
||||
({ \
|
||||
bool _ret = unlikely(!!(cond)); \
|
||||
\
|
||||
if (_ret) \
|
||||
bch2_trans_inconsistent(trans, __VA_ARGS__); \
|
||||
bch2_trans_inconsistent(__VA_ARGS__); \
|
||||
_ret; \
|
||||
})
|
||||
|
||||
int __bch2_topology_error(struct bch_fs *, struct printbuf *);
|
||||
__printf(2, 3)
|
||||
int bch2_fs_topology_error(struct bch_fs *, const char *, ...);
|
||||
|
||||
/*
|
||||
* Fsck errors: inconsistency errors we detect at mount time, and should ideally
|
||||
* be able to repair:
|
||||
@ -80,7 +66,7 @@ int bch2_topology_error(struct bch_fs *);
|
||||
|
||||
struct fsck_err_state {
|
||||
struct list_head list;
|
||||
const char *fmt;
|
||||
enum bch_sb_error_id id;
|
||||
u64 nr;
|
||||
bool ratelimited;
|
||||
int ret;
|
||||
@ -90,6 +76,12 @@ struct fsck_err_state {
|
||||
|
||||
#define fsck_err_count(_c, _err) bch2_sb_err_count(_c, BCH_FSCK_ERR_##_err)
|
||||
|
||||
void __bch2_count_fsck_err(struct bch_fs *,
|
||||
enum bch_sb_error_id, const char *,
|
||||
bool *, bool *, bool *);
|
||||
#define bch2_count_fsck_err(_c, _err, ...) \
|
||||
__bch2_count_fsck_err(_c, BCH_FSCK_ERR_##_err, __VA_ARGS__)
|
||||
|
||||
__printf(5, 6) __cold
|
||||
int __bch2_fsck_err(struct bch_fs *, struct btree_trans *,
|
||||
enum bch_fsck_flags,
|
||||
|
@ -227,8 +227,11 @@ int bch2_bkey_pick_read_device(struct bch_fs *c, struct bkey_s_c k,
|
||||
if (have_io_errors)
|
||||
return -BCH_ERR_data_read_io_err;
|
||||
|
||||
WARN_ONCE(1, "unhandled error case in %s\n", __func__);
|
||||
return -EINVAL;
|
||||
/*
|
||||
* If we get here, we have pointers (bkey_ptrs_validate() ensures that),
|
||||
* but they don't point to valid devices:
|
||||
*/
|
||||
return -BCH_ERR_no_devices_valid;
|
||||
}
|
||||
|
||||
/* KEY_TYPE_btree_ptr: */
|
||||
@ -592,29 +595,35 @@ static void bch2_extent_crc_pack(union bch_extent_crc *dst,
|
||||
struct bch_extent_crc_unpacked src,
|
||||
enum bch_extent_entry_type type)
|
||||
{
|
||||
#define set_common_fields(_dst, _src) \
|
||||
_dst.type = 1 << type; \
|
||||
_dst.csum_type = _src.csum_type, \
|
||||
_dst.compression_type = _src.compression_type, \
|
||||
_dst._compressed_size = _src.compressed_size - 1, \
|
||||
_dst._uncompressed_size = _src.uncompressed_size - 1, \
|
||||
_dst.offset = _src.offset
|
||||
#define common_fields(_src) \
|
||||
.type = BIT(type), \
|
||||
.csum_type = _src.csum_type, \
|
||||
.compression_type = _src.compression_type, \
|
||||
._compressed_size = _src.compressed_size - 1, \
|
||||
._uncompressed_size = _src.uncompressed_size - 1, \
|
||||
.offset = _src.offset
|
||||
|
||||
switch (type) {
|
||||
case BCH_EXTENT_ENTRY_crc32:
|
||||
set_common_fields(dst->crc32, src);
|
||||
dst->crc32.csum = (u32 __force) *((__le32 *) &src.csum.lo);
|
||||
dst->crc32 = (struct bch_extent_crc32) {
|
||||
common_fields(src),
|
||||
.csum = (u32 __force) *((__le32 *) &src.csum.lo),
|
||||
};
|
||||
break;
|
||||
case BCH_EXTENT_ENTRY_crc64:
|
||||
set_common_fields(dst->crc64, src);
|
||||
dst->crc64.nonce = src.nonce;
|
||||
dst->crc64.csum_lo = (u64 __force) src.csum.lo;
|
||||
dst->crc64.csum_hi = (u64 __force) *((__le16 *) &src.csum.hi);
|
||||
dst->crc64 = (struct bch_extent_crc64) {
|
||||
common_fields(src),
|
||||
.nonce = src.nonce,
|
||||
.csum_lo = (u64 __force) src.csum.lo,
|
||||
.csum_hi = (u64 __force) *((__le16 *) &src.csum.hi),
|
||||
};
|
||||
break;
|
||||
case BCH_EXTENT_ENTRY_crc128:
|
||||
set_common_fields(dst->crc128, src);
|
||||
dst->crc128.nonce = src.nonce;
|
||||
dst->crc128.csum = src.csum;
|
||||
dst->crc128 = (struct bch_extent_crc128) {
|
||||
common_fields(src),
|
||||
.nonce = src.nonce,
|
||||
.csum = src.csum,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
|
@ -225,11 +225,11 @@ static void bchfs_read(struct btree_trans *trans,
|
||||
|
||||
bch2_read_extent(trans, rbio, iter.pos,
|
||||
data_btree, k, offset_into_extent, flags);
|
||||
swap(rbio->bio.bi_iter.bi_size, bytes);
|
||||
|
||||
if (flags & BCH_READ_last_fragment)
|
||||
break;
|
||||
|
||||
swap(rbio->bio.bi_iter.bi_size, bytes);
|
||||
bio_advance(&rbio->bio, bytes);
|
||||
err:
|
||||
if (ret &&
|
||||
|
@ -999,17 +999,28 @@ static loff_t bch2_seek_hole(struct file *file, u64 offset)
|
||||
POS(inode->v.i_ino, offset >> 9),
|
||||
POS(inode->v.i_ino, U64_MAX),
|
||||
inum.subvol, BTREE_ITER_slots, k, ({
|
||||
if (k.k->p.inode != inode->v.i_ino) {
|
||||
next_hole = bch2_seek_pagecache_hole(&inode->v,
|
||||
offset, MAX_LFS_FILESIZE, 0, false);
|
||||
break;
|
||||
} else if (!bkey_extent_is_data(k.k)) {
|
||||
next_hole = bch2_seek_pagecache_hole(&inode->v,
|
||||
max(offset, bkey_start_offset(k.k) << 9),
|
||||
k.k->p.offset << 9, 0, false);
|
||||
if (k.k->p.inode != inode->v.i_ino ||
|
||||
!bkey_extent_is_data(k.k)) {
|
||||
loff_t start_offset = k.k->p.inode == inode->v.i_ino
|
||||
? max(offset, bkey_start_offset(k.k) << 9)
|
||||
: offset;
|
||||
loff_t end_offset = k.k->p.inode == inode->v.i_ino
|
||||
? MAX_LFS_FILESIZE
|
||||
: k.k->p.offset << 9;
|
||||
|
||||
if (next_hole < k.k->p.offset << 9)
|
||||
/*
|
||||
* Found a hole in the btree, now make sure it's
|
||||
* a hole in the pagecache. We might have to
|
||||
* keep searching if this hole is entirely dirty
|
||||
* in the page cache:
|
||||
*/
|
||||
bch2_trans_unlock(trans);
|
||||
loff_t pagecache_hole = bch2_seek_pagecache_hole(&inode->v,
|
||||
start_offset, end_offset, 0, false);
|
||||
if (pagecache_hole < end_offset) {
|
||||
next_hole = pagecache_hole;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
offset = max(offset, bkey_start_offset(k.k) << 9);
|
||||
}
|
||||
|
@ -5,8 +5,8 @@
|
||||
#include "chardev.h"
|
||||
#include "dirent.h"
|
||||
#include "fs.h"
|
||||
#include "fs-common.h"
|
||||
#include "fs-ioctl.h"
|
||||
#include "namei.h"
|
||||
#include "quota.h"
|
||||
|
||||
#include <linux/compat.h>
|
||||
@ -541,10 +541,12 @@ static long bch2_ioctl_subvolume_destroy(struct bch_fs *c, struct file *filp,
|
||||
ret = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
ret = __bch2_unlink(dir, victim, true);
|
||||
|
||||
ret = inode_permission(file_mnt_idmap(filp), d_inode(victim), MAY_WRITE) ?:
|
||||
__bch2_unlink(dir, victim, true);
|
||||
if (!ret) {
|
||||
fsnotify_rmdir(dir, victim);
|
||||
d_delete(victim);
|
||||
d_invalidate(victim);
|
||||
}
|
||||
err:
|
||||
inode_unlock(dir);
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "errcode.h"
|
||||
#include "extents.h"
|
||||
#include "fs.h"
|
||||
#include "fs-common.h"
|
||||
#include "fs-io.h"
|
||||
#include "fs-ioctl.h"
|
||||
#include "fs-io-buffered.h"
|
||||
@ -22,6 +21,7 @@
|
||||
#include "io_read.h"
|
||||
#include "journal.h"
|
||||
#include "keylist.h"
|
||||
#include "namei.h"
|
||||
#include "quota.h"
|
||||
#include "rebalance.h"
|
||||
#include "snapshot.h"
|
||||
@ -641,7 +641,9 @@ static struct bch_inode_info *bch2_lookup_trans(struct btree_trans *trans,
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = bch2_dirent_read_target(trans, dir, bkey_s_c_to_dirent(k), &inum);
|
||||
struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
|
||||
|
||||
ret = bch2_dirent_read_target(trans, dir, d, &inum);
|
||||
if (ret > 0)
|
||||
ret = -ENOENT;
|
||||
if (ret)
|
||||
@ -651,30 +653,30 @@ static struct bch_inode_info *bch2_lookup_trans(struct btree_trans *trans,
|
||||
if (inode)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Note: if check/repair needs it, we commit before
|
||||
* bch2_inode_hash_init_insert(), as after that point we can't take a
|
||||
* restart - not in the top level loop with a commit_do(), like we
|
||||
* usually do:
|
||||
*/
|
||||
|
||||
struct bch_subvolume subvol;
|
||||
struct bch_inode_unpacked inode_u;
|
||||
ret = bch2_subvolume_get(trans, inum.subvol, true, &subvol) ?:
|
||||
bch2_inode_find_by_inum_nowarn_trans(trans, inum, &inode_u) ?:
|
||||
bch2_check_dirent_target(trans, &dirent_iter, d, &inode_u, false) ?:
|
||||
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?:
|
||||
PTR_ERR_OR_ZERO(inode = bch2_inode_hash_init_insert(trans, inum, &inode_u, &subvol));
|
||||
|
||||
/*
|
||||
* don't remove it: check_inodes might find another inode that points
|
||||
* back to this dirent
|
||||
*/
|
||||
bch2_fs_inconsistent_on(bch2_err_matches(ret, ENOENT),
|
||||
c, "dirent to missing inode:\n %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf));
|
||||
c, "dirent to missing inode:\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* regular files may have hardlinks: */
|
||||
if (bch2_fs_inconsistent_on(bch2_inode_should_have_single_bp(&inode_u) &&
|
||||
!bkey_eq(k.k->p, POS(inode_u.bi_dir, inode_u.bi_dir_offset)),
|
||||
c,
|
||||
"dirent points to inode that does not point back:\n %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k),
|
||||
prt_printf(&buf, "\n "),
|
||||
bch2_inode_unpacked_to_text(&buf, &inode_u),
|
||||
buf.buf))) {
|
||||
ret = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
out:
|
||||
bch2_trans_iter_exit(trans, &dirent_iter);
|
||||
printbuf_exit(&buf);
|
||||
@ -2177,7 +2179,7 @@ static int bch2_fs_get_tree(struct fs_context *fc)
|
||||
|
||||
/* Some options can't be parsed until after the fs is started: */
|
||||
opts = bch2_opts_empty();
|
||||
ret = bch2_parse_mount_opts(c, &opts, NULL, opts_parse->parse_later.buf);
|
||||
ret = bch2_parse_mount_opts(c, &opts, NULL, opts_parse->parse_later.buf, false);
|
||||
if (ret)
|
||||
goto err_stop_fs;
|
||||
|
||||
@ -2288,7 +2290,8 @@ err_stop_fs:
|
||||
goto err;
|
||||
|
||||
err_put_super:
|
||||
__bch2_fs_stop(c);
|
||||
if (!sb->s_root)
|
||||
__bch2_fs_stop(c);
|
||||
deactivate_locked_super(sb);
|
||||
goto err;
|
||||
}
|
||||
@ -2331,6 +2334,8 @@ static int bch2_fs_parse_param(struct fs_context *fc,
|
||||
int ret = bch2_parse_one_mount_opt(c, &opts->opts,
|
||||
&opts->parse_later, param->key,
|
||||
param->string);
|
||||
if (ret)
|
||||
pr_err("Error parsing option %s: %s", param->key, bch2_err_str(ret));
|
||||
|
||||
return bch2_err_class(ret);
|
||||
}
|
||||
|
@ -10,10 +10,10 @@
|
||||
#include "dirent.h"
|
||||
#include "error.h"
|
||||
#include "fs.h"
|
||||
#include "fs-common.h"
|
||||
#include "fsck.h"
|
||||
#include "inode.h"
|
||||
#include "keylist.h"
|
||||
#include "namei.h"
|
||||
#include "recovery_passes.h"
|
||||
#include "snapshot.h"
|
||||
#include "super.h"
|
||||
@ -23,13 +23,6 @@
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/dcache.h> /* struct qstr */
|
||||
|
||||
static bool inode_points_to_dirent(struct bch_inode_unpacked *inode,
|
||||
struct bkey_s_c_dirent d)
|
||||
{
|
||||
return inode->bi_dir == d.k->p.inode &&
|
||||
inode->bi_dir_offset == d.k->p.offset;
|
||||
}
|
||||
|
||||
static int dirent_points_to_inode_nowarn(struct bkey_s_c_dirent d,
|
||||
struct bch_inode_unpacked *inode)
|
||||
{
|
||||
@ -116,29 +109,6 @@ static int subvol_lookup(struct btree_trans *trans, u32 subvol,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lookup_first_inode(struct btree_trans *trans, u64 inode_nr,
|
||||
struct bch_inode_unpacked *inode)
|
||||
{
|
||||
struct btree_iter iter;
|
||||
struct bkey_s_c k;
|
||||
int ret;
|
||||
|
||||
for_each_btree_key_norestart(trans, iter, BTREE_ID_inodes, POS(0, inode_nr),
|
||||
BTREE_ITER_all_snapshots, k, ret) {
|
||||
if (k.k->p.offset != inode_nr)
|
||||
break;
|
||||
if (!bkey_is_inode(k.k))
|
||||
continue;
|
||||
ret = bch2_inode_unpack(k, inode);
|
||||
goto found;
|
||||
}
|
||||
ret = -BCH_ERR_ENOENT_inode;
|
||||
found:
|
||||
bch_err_msg(trans->c, ret, "fetching inode %llu", inode_nr);
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lookup_inode(struct btree_trans *trans, u64 inode_nr, u32 snapshot,
|
||||
struct bch_inode_unpacked *inode)
|
||||
{
|
||||
@ -179,32 +149,6 @@ static int lookup_dirent_in_snapshot(struct btree_trans *trans,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __remove_dirent(struct btree_trans *trans, struct bpos pos)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct btree_iter iter;
|
||||
struct bch_inode_unpacked dir_inode;
|
||||
struct bch_hash_info dir_hash_info;
|
||||
int ret;
|
||||
|
||||
ret = lookup_first_inode(trans, pos.inode, &dir_inode);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dir_hash_info = bch2_hash_info_init(c, &dir_inode);
|
||||
|
||||
bch2_trans_iter_init(trans, &iter, BTREE_ID_dirents, pos, BTREE_ITER_intent);
|
||||
|
||||
ret = bch2_btree_iter_traverse(&iter) ?:
|
||||
bch2_hash_delete_at(trans, bch2_dirent_hash_desc,
|
||||
&dir_hash_info, &iter,
|
||||
BTREE_UPDATE_internal_snapshot_node);
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
err:
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find any subvolume associated with a tree of snapshots
|
||||
* We can't rely on master_subvol - it might have been deleted.
|
||||
@ -548,7 +492,7 @@ static int remove_backpointer(struct btree_trans *trans,
|
||||
SPOS(inode->bi_dir, inode->bi_dir_offset, inode->bi_snapshot));
|
||||
int ret = bkey_err(d) ?:
|
||||
dirent_points_to_inode(c, d, inode) ?:
|
||||
__remove_dirent(trans, d.k->p);
|
||||
bch2_fsck_remove_dirent(trans, d.k->p);
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
return ret;
|
||||
}
|
||||
@ -1477,14 +1421,14 @@ static int check_key_has_inode(struct btree_trans *trans,
|
||||
|
||||
if (fsck_err_on(!i,
|
||||
trans, key_in_missing_inode,
|
||||
"key in missing inode:\n %s",
|
||||
"key in missing inode:\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
||||
goto delete;
|
||||
|
||||
if (fsck_err_on(i && !btree_matches_i_mode(iter->btree_id, i->inode.bi_mode),
|
||||
trans, key_in_wrong_inode_type,
|
||||
"key for wrong inode mode %o:\n %s",
|
||||
"key for wrong inode mode %o:\n%s",
|
||||
i->inode.bi_mode,
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
||||
@ -1627,13 +1571,13 @@ static int overlapping_extents_found(struct btree_trans *trans,
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
prt_str(&buf, "\n ");
|
||||
prt_newline(&buf);
|
||||
bch2_bkey_val_to_text(&buf, c, k1);
|
||||
|
||||
if (!bpos_eq(pos1, k1.k->p)) {
|
||||
prt_str(&buf, "\n wanted\n ");
|
||||
prt_str(&buf, "\nwanted\n ");
|
||||
bch2_bpos_to_text(&buf, pos1);
|
||||
prt_str(&buf, "\n ");
|
||||
prt_str(&buf, "\n");
|
||||
bch2_bkey_to_text(&buf, &pos2);
|
||||
|
||||
bch_err(c, "%s: error finding first overlapping extent when repairing, got%s",
|
||||
@ -1656,7 +1600,7 @@ static int overlapping_extents_found(struct btree_trans *trans,
|
||||
break;
|
||||
}
|
||||
|
||||
prt_str(&buf, "\n ");
|
||||
prt_newline(&buf);
|
||||
bch2_bkey_val_to_text(&buf, c, k2);
|
||||
|
||||
if (bpos_gt(k2.k->p, pos2.p) ||
|
||||
@ -1667,7 +1611,7 @@ static int overlapping_extents_found(struct btree_trans *trans,
|
||||
goto err;
|
||||
}
|
||||
|
||||
prt_printf(&buf, "\n overwriting %s extent",
|
||||
prt_printf(&buf, "\noverwriting %s extent",
|
||||
pos1.snapshot >= pos2.p.snapshot ? "first" : "second");
|
||||
|
||||
if (fsck_err(trans, extent_overlapping,
|
||||
@ -1688,6 +1632,8 @@ static int overlapping_extents_found(struct btree_trans *trans,
|
||||
bch2_trans_commit(trans, &res, NULL, BCH_TRANS_COMMIT_no_enospc);
|
||||
bch2_disk_reservation_put(c, &res);
|
||||
|
||||
bch_info(c, "repair ret %s", bch2_err_str(ret));
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
@ -1840,7 +1786,7 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
if (fsck_err_on(k.k->p.offset > round_up(i->inode.bi_size, block_bytes(c)) >> 9 &&
|
||||
!bkey_extent_is_reservation(k),
|
||||
trans, extent_past_end_of_inode,
|
||||
"extent type past end of inode %llu:%u, i_size %llu\n %s",
|
||||
"extent type past end of inode %llu:%u, i_size %llu\n%s",
|
||||
i->inode.bi_inum, i->snapshot, i->inode.bi_size,
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
struct btree_iter iter2;
|
||||
@ -1985,169 +1931,6 @@ static int check_subdir_dirents_count(struct btree_trans *trans, struct inode_wa
|
||||
trans_was_restarted(trans, restart_count);
|
||||
}
|
||||
|
||||
noinline_for_stack
|
||||
static int check_dirent_inode_dirent(struct btree_trans *trans,
|
||||
struct btree_iter *iter,
|
||||
struct bkey_s_c_dirent d,
|
||||
struct bch_inode_unpacked *target)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
struct btree_iter bp_iter = { NULL };
|
||||
int ret = 0;
|
||||
|
||||
if (inode_points_to_dirent(target, d))
|
||||
return 0;
|
||||
|
||||
if (!target->bi_dir &&
|
||||
!target->bi_dir_offset) {
|
||||
fsck_err_on(S_ISDIR(target->bi_mode),
|
||||
trans, inode_dir_missing_backpointer,
|
||||
"directory with missing backpointer\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, d.s_c),
|
||||
prt_printf(&buf, "\n"),
|
||||
bch2_inode_unpacked_to_text(&buf, target),
|
||||
buf.buf));
|
||||
|
||||
fsck_err_on(target->bi_flags & BCH_INODE_unlinked,
|
||||
trans, inode_unlinked_but_has_dirent,
|
||||
"inode unlinked but has dirent\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, d.s_c),
|
||||
prt_printf(&buf, "\n"),
|
||||
bch2_inode_unpacked_to_text(&buf, target),
|
||||
buf.buf));
|
||||
|
||||
target->bi_flags &= ~BCH_INODE_unlinked;
|
||||
target->bi_dir = d.k->p.inode;
|
||||
target->bi_dir_offset = d.k->p.offset;
|
||||
return __bch2_fsck_write_inode(trans, target);
|
||||
}
|
||||
|
||||
if (bch2_inode_should_have_single_bp(target) &&
|
||||
!fsck_err(trans, inode_wrong_backpointer,
|
||||
"dirent points to inode that does not point back:\n %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, d.s_c),
|
||||
prt_printf(&buf, "\n "),
|
||||
bch2_inode_unpacked_to_text(&buf, target),
|
||||
buf.buf)))
|
||||
goto err;
|
||||
|
||||
struct bkey_s_c_dirent bp_dirent = dirent_get_by_pos(trans, &bp_iter,
|
||||
SPOS(target->bi_dir, target->bi_dir_offset, target->bi_snapshot));
|
||||
ret = bkey_err(bp_dirent);
|
||||
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||
goto err;
|
||||
|
||||
bool backpointer_exists = !ret;
|
||||
ret = 0;
|
||||
|
||||
if (fsck_err_on(!backpointer_exists,
|
||||
trans, inode_wrong_backpointer,
|
||||
"inode %llu:%u has wrong backpointer:\n"
|
||||
"got %llu:%llu\n"
|
||||
"should be %llu:%llu",
|
||||
target->bi_inum, target->bi_snapshot,
|
||||
target->bi_dir,
|
||||
target->bi_dir_offset,
|
||||
d.k->p.inode,
|
||||
d.k->p.offset)) {
|
||||
target->bi_dir = d.k->p.inode;
|
||||
target->bi_dir_offset = d.k->p.offset;
|
||||
ret = __bch2_fsck_write_inode(trans, target);
|
||||
goto out;
|
||||
}
|
||||
|
||||
bch2_bkey_val_to_text(&buf, c, d.s_c);
|
||||
prt_newline(&buf);
|
||||
if (backpointer_exists)
|
||||
bch2_bkey_val_to_text(&buf, c, bp_dirent.s_c);
|
||||
|
||||
if (fsck_err_on(backpointer_exists &&
|
||||
(S_ISDIR(target->bi_mode) ||
|
||||
target->bi_subvol),
|
||||
trans, inode_dir_multiple_links,
|
||||
"%s %llu:%u with multiple links\n%s",
|
||||
S_ISDIR(target->bi_mode) ? "directory" : "subvolume",
|
||||
target->bi_inum, target->bi_snapshot, buf.buf)) {
|
||||
ret = __remove_dirent(trans, d.k->p);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* hardlinked file with nlink 0:
|
||||
* We're just adjusting nlink here so check_nlinks() will pick
|
||||
* it up, it ignores inodes with nlink 0
|
||||
*/
|
||||
if (fsck_err_on(backpointer_exists && !target->bi_nlink,
|
||||
trans, inode_multiple_links_but_nlink_0,
|
||||
"inode %llu:%u type %s has multiple links but i_nlink 0\n%s",
|
||||
target->bi_inum, target->bi_snapshot, bch2_d_types[d.v->d_type], buf.buf)) {
|
||||
target->bi_nlink++;
|
||||
target->bi_flags &= ~BCH_INODE_unlinked;
|
||||
ret = __bch2_fsck_write_inode(trans, target);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
out:
|
||||
err:
|
||||
fsck_err:
|
||||
bch2_trans_iter_exit(trans, &bp_iter);
|
||||
printbuf_exit(&buf);
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
noinline_for_stack
|
||||
static int check_dirent_target(struct btree_trans *trans,
|
||||
struct btree_iter *iter,
|
||||
struct bkey_s_c_dirent d,
|
||||
struct bch_inode_unpacked *target)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct bkey_i_dirent *n;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
int ret = 0;
|
||||
|
||||
ret = check_dirent_inode_dirent(trans, iter, d, target);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (fsck_err_on(d.v->d_type != inode_d_type(target),
|
||||
trans, dirent_d_type_wrong,
|
||||
"incorrect d_type: got %s, should be %s:\n%s",
|
||||
bch2_d_type_str(d.v->d_type),
|
||||
bch2_d_type_str(inode_d_type(target)),
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf))) {
|
||||
n = bch2_trans_kmalloc(trans, bkey_bytes(d.k));
|
||||
ret = PTR_ERR_OR_ZERO(n);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
bkey_reassemble(&n->k_i, d.s_c);
|
||||
n->v.d_type = inode_d_type(target);
|
||||
if (n->v.d_type == DT_SUBVOL) {
|
||||
n->v.d_parent_subvol = cpu_to_le32(target->bi_parent_subvol);
|
||||
n->v.d_child_subvol = cpu_to_le32(target->bi_subvol);
|
||||
} else {
|
||||
n->v.d_inum = cpu_to_le64(target->bi_inum);
|
||||
}
|
||||
|
||||
ret = bch2_trans_update(trans, iter, &n->k_i, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
d = dirent_i_to_s_c(n);
|
||||
}
|
||||
err:
|
||||
fsck_err:
|
||||
printbuf_exit(&buf);
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* find a subvolume that's a descendent of @snapshot: */
|
||||
static int find_snapshot_subvol(struct btree_trans *trans, u32 snapshot, u32 *subvolid)
|
||||
{
|
||||
@ -2247,7 +2030,7 @@ static int check_dirent_to_subvol(struct btree_trans *trans, struct btree_iter *
|
||||
if (fsck_err(trans, dirent_to_missing_subvol,
|
||||
"dirent points to missing subvolume\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf)))
|
||||
return __remove_dirent(trans, d.k->p);
|
||||
return bch2_fsck_remove_dirent(trans, d.k->p);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
@ -2291,7 +2074,7 @@ static int check_dirent_to_subvol(struct btree_trans *trans, struct btree_iter *
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = check_dirent_target(trans, iter, d, &subvol_root);
|
||||
ret = bch2_check_dirent_target(trans, iter, d, &subvol_root, true);
|
||||
if (ret)
|
||||
goto err;
|
||||
out:
|
||||
@ -2378,13 +2161,13 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k),
|
||||
buf.buf))) {
|
||||
ret = __remove_dirent(trans, d.k->p);
|
||||
ret = bch2_fsck_remove_dirent(trans, d.k->p);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
darray_for_each(target->inodes, i) {
|
||||
ret = check_dirent_target(trans, iter, d, &i->inode);
|
||||
ret = bch2_check_dirent_target(trans, iter, d, &i->inode, true);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
@ -3240,7 +3023,7 @@ long bch2_ioctl_fsck_offline(struct bch_ioctl_fsck_offline __user *user_arg)
|
||||
if (arg.opts) {
|
||||
char *optstr = strndup_user((char __user *)(unsigned long) arg.opts, 1 << 16);
|
||||
ret = PTR_ERR_OR_ZERO(optstr) ?:
|
||||
bch2_parse_mount_opts(NULL, &thr->opts, NULL, optstr);
|
||||
bch2_parse_mount_opts(NULL, &thr->opts, NULL, optstr, false);
|
||||
if (!IS_ERR(optstr))
|
||||
kfree(optstr);
|
||||
|
||||
@ -3348,7 +3131,7 @@ long bch2_ioctl_fsck_online(struct bch_fs *c, struct bch_ioctl_fsck_online arg)
|
||||
char *optstr = strndup_user((char __user *)(unsigned long) arg.opts, 1 << 16);
|
||||
|
||||
ret = PTR_ERR_OR_ZERO(optstr) ?:
|
||||
bch2_parse_mount_opts(c, &thr->opts, NULL, optstr);
|
||||
bch2_parse_mount_opts(c, &thr->opts, NULL, optstr, false);
|
||||
if (!IS_ERR(optstr))
|
||||
kfree(optstr);
|
||||
|
||||
|
@ -731,10 +731,9 @@ int bch2_trigger_inode(struct btree_trans *trans,
|
||||
bkey_s_to_inode_v3(new).v->bi_journal_seq = cpu_to_le64(trans->journal_res.seq);
|
||||
}
|
||||
|
||||
s64 nr = bkey_is_inode(new.k) - bkey_is_inode(old.k);
|
||||
if ((flags & (BTREE_TRIGGER_transactional|BTREE_TRIGGER_gc)) && nr) {
|
||||
struct disk_accounting_pos acc = { .type = BCH_DISK_ACCOUNTING_nr_inodes };
|
||||
int ret = bch2_disk_accounting_mod(trans, &acc, &nr, 1, flags & BTREE_TRIGGER_gc);
|
||||
s64 nr[1] = { bkey_is_inode(new.k) - bkey_is_inode(old.k) };
|
||||
if ((flags & (BTREE_TRIGGER_transactional|BTREE_TRIGGER_gc)) && nr[0]) {
|
||||
int ret = bch2_disk_accounting_mod2(trans, flags & BTREE_TRIGGER_gc, nr, nr_inodes);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -1079,7 +1078,7 @@ retry:
|
||||
bch2_fs_inconsistent(c,
|
||||
"inode %llu:%u not found when deleting",
|
||||
inum.inum, snapshot);
|
||||
ret = -EIO;
|
||||
ret = -BCH_ERR_ENOENT_inode;
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -1243,7 +1242,7 @@ retry:
|
||||
bch2_fs_inconsistent(c,
|
||||
"inode %llu:%u not found when deleting",
|
||||
inum, snapshot);
|
||||
ret = -EIO;
|
||||
ret = -BCH_ERR_ENOENT_inode;
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -277,6 +277,7 @@ static inline bool bch2_inode_should_have_single_bp(struct bch_inode_unpacked *i
|
||||
bool inode_has_bp = inode->bi_dir || inode->bi_dir_offset;
|
||||
|
||||
return S_ISDIR(inode->bi_mode) ||
|
||||
inode->bi_subvol ||
|
||||
(!inode->bi_nlink && inode_has_bp);
|
||||
}
|
||||
|
||||
|
@ -259,6 +259,7 @@ static struct bch_read_bio *__promote_alloc(struct btree_trans *trans,
|
||||
&orig->opts,
|
||||
update_opts,
|
||||
btree_id, k);
|
||||
op->write.type = BCH_DATA_UPDATE_promote;
|
||||
/*
|
||||
* possible errors: -BCH_ERR_nocow_lock_blocked,
|
||||
* -BCH_ERR_ENOSPC_disk_reservation:
|
||||
@ -1132,10 +1133,6 @@ retry_pick:
|
||||
rbio->bio.bi_iter.bi_sector = pick.ptr.offset;
|
||||
rbio->bio.bi_end_io = bch2_read_endio;
|
||||
|
||||
/* XXX: also nvme read recovery level */
|
||||
if (unlikely(failed && bch2_dev_io_failures(failed, pick.ptr.dev)))
|
||||
rbio->bio.bi_opf |= REQ_FUA;
|
||||
|
||||
if (rbio->bounce)
|
||||
trace_and_count(c, io_read_bounce, &rbio->bio);
|
||||
|
||||
@ -1326,13 +1323,14 @@ int __bch2_read(struct btree_trans *trans, struct bch_read_bio *rbio,
|
||||
ret = __bch2_read_extent(trans, rbio, bvec_iter, iter.pos,
|
||||
data_btree, k,
|
||||
offset_into_extent, failed, flags, -1);
|
||||
swap(bvec_iter.bi_size, bytes);
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (flags & BCH_READ_last_fragment)
|
||||
break;
|
||||
|
||||
swap(bvec_iter.bi_size, bytes);
|
||||
bio_advance_iter(&rbio->bio, &bvec_iter, bytes);
|
||||
err:
|
||||
if (ret == -BCH_ERR_data_read_retry_csum_err_maybe_userspace)
|
||||
|
@ -137,8 +137,10 @@ static inline void bch2_read_extent(struct btree_trans *trans,
|
||||
enum btree_id data_btree, struct bkey_s_c k,
|
||||
unsigned offset_into_extent, unsigned flags)
|
||||
{
|
||||
__bch2_read_extent(trans, rbio, rbio->bio.bi_iter, read_pos,
|
||||
data_btree, k, offset_into_extent, NULL, flags, -1);
|
||||
int ret = __bch2_read_extent(trans, rbio, rbio->bio.bi_iter, read_pos,
|
||||
data_btree, k, offset_into_extent, NULL, flags, -1);
|
||||
/* __bch2_read_extent only returns errors if BCH_READ_in_retry is set */
|
||||
WARN(ret, "unhandled error from __bch2_read_extent()");
|
||||
}
|
||||
|
||||
int __bch2_read(struct btree_trans *, struct bch_read_bio *, struct bvec_iter,
|
||||
|
@ -402,61 +402,36 @@ static int bch2_write_index_default(struct bch_write_op *op)
|
||||
|
||||
/* Writes */
|
||||
|
||||
void bch2_write_op_error_trans(struct btree_trans *trans, struct printbuf *out,
|
||||
struct bch_write_op *op, u64 offset, const char *fmt, ...)
|
||||
void bch2_write_op_error(struct bch_write_op *op, u64 offset, const char *fmt, ...)
|
||||
{
|
||||
if (op->subvol)
|
||||
lockrestart_do(trans,
|
||||
bch2_inum_offset_err_msg_trans(trans, out,
|
||||
(subvol_inum) { op->subvol, op->pos.inode, },
|
||||
offset << 9));
|
||||
else {
|
||||
struct bpos pos = op->pos;
|
||||
pos.offset = offset;
|
||||
lockrestart_do(trans, bch2_inum_snap_offset_err_msg_trans(trans, out, pos));
|
||||
}
|
||||
struct printbuf buf = PRINTBUF;
|
||||
|
||||
prt_str(out, "write error: ");
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
prt_vprintf(out, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (op->flags & BCH_WRITE_move) {
|
||||
struct data_update *u = container_of(op, struct data_update, op);
|
||||
|
||||
prt_printf(out, "\n from internal move ");
|
||||
bch2_bkey_val_to_text(out, op->c, bkey_i_to_s_c(u->k.k));
|
||||
}
|
||||
}
|
||||
|
||||
void bch2_write_op_error(struct printbuf *out, struct bch_write_op *op, u64 offset,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
if (op->subvol)
|
||||
bch2_inum_offset_err_msg(op->c, out,
|
||||
if (op->subvol) {
|
||||
bch2_inum_offset_err_msg(op->c, &buf,
|
||||
(subvol_inum) { op->subvol, op->pos.inode, },
|
||||
offset << 9);
|
||||
else {
|
||||
} else {
|
||||
struct bpos pos = op->pos;
|
||||
pos.offset = offset;
|
||||
bch2_inum_snap_offset_err_msg(op->c, out, pos);
|
||||
bch2_inum_snap_offset_err_msg(op->c, &buf, pos);
|
||||
}
|
||||
|
||||
prt_str(out, "write error: ");
|
||||
prt_str(&buf, "write error: ");
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
prt_vprintf(out, fmt, args);
|
||||
prt_vprintf(&buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (op->flags & BCH_WRITE_move) {
|
||||
struct data_update *u = container_of(op, struct data_update, op);
|
||||
|
||||
prt_printf(out, "\n from internal move ");
|
||||
bch2_bkey_val_to_text(out, op->c, bkey_i_to_s_c(u->k.k));
|
||||
prt_printf(&buf, "\n from internal move ");
|
||||
bch2_bkey_val_to_text(&buf, op->c, bkey_i_to_s_c(u->k.k));
|
||||
}
|
||||
|
||||
bch_err_ratelimited(op->c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
|
||||
void bch2_submit_wbio_replicas(struct bch_write_bio *wbio, struct bch_fs *c,
|
||||
@ -554,7 +529,7 @@ static noinline int bch2_write_drop_io_error_ptrs(struct bch_write_op *op)
|
||||
test_bit(ptr->dev, op->failed.d));
|
||||
|
||||
if (!bch2_bkey_nr_ptrs(bkey_i_to_s_c(src)))
|
||||
return -EIO;
|
||||
return -BCH_ERR_data_write_io;
|
||||
}
|
||||
|
||||
if (dst != src)
|
||||
@ -598,11 +573,8 @@ static void __bch2_write_index(struct bch_write_op *op)
|
||||
if (unlikely(ret && !bch2_err_matches(ret, EROFS))) {
|
||||
struct bkey_i *insert = bch2_keylist_front(&op->insert_keys);
|
||||
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_write_op_error(&buf, op, bkey_start_offset(&insert->k),
|
||||
bch2_write_op_error(op, bkey_start_offset(&insert->k),
|
||||
"btree update error: %s", bch2_err_str(ret));
|
||||
bch_err_ratelimited(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
@ -611,7 +583,7 @@ static void __bch2_write_index(struct bch_write_op *op)
|
||||
out:
|
||||
/* If some a bucket wasn't written, we can't erasure code it: */
|
||||
for_each_set_bit(dev, op->failed.d, BCH_SB_MEMBERS_MAX)
|
||||
bch2_open_bucket_write_error(c, &op->open_buckets, dev);
|
||||
bch2_open_bucket_write_error(c, &op->open_buckets, dev, -BCH_ERR_data_write_io);
|
||||
|
||||
bch2_open_buckets_put(c, &op->open_buckets);
|
||||
return;
|
||||
@ -837,7 +809,6 @@ static int bch2_write_rechecksum(struct bch_fs *c,
|
||||
{
|
||||
struct bio *bio = &op->wbio.bio;
|
||||
struct bch_extent_crc_unpacked new_crc;
|
||||
int ret;
|
||||
|
||||
/* bch2_rechecksum_bio() can't encrypt or decrypt data: */
|
||||
|
||||
@ -845,10 +816,10 @@ static int bch2_write_rechecksum(struct bch_fs *c,
|
||||
bch2_csum_type_is_encryption(new_csum_type))
|
||||
new_csum_type = op->crc.csum_type;
|
||||
|
||||
ret = bch2_rechecksum_bio(c, bio, op->version, op->crc,
|
||||
NULL, &new_crc,
|
||||
op->crc.offset, op->crc.live_size,
|
||||
new_csum_type);
|
||||
int ret = bch2_rechecksum_bio(c, bio, op->version, op->crc,
|
||||
NULL, &new_crc,
|
||||
op->crc.offset, op->crc.live_size,
|
||||
new_csum_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -858,44 +829,12 @@ static int bch2_write_rechecksum(struct bch_fs *c,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bch2_write_decrypt(struct bch_write_op *op)
|
||||
{
|
||||
struct bch_fs *c = op->c;
|
||||
struct nonce nonce = extent_nonce(op->version, op->crc);
|
||||
struct bch_csum csum;
|
||||
int ret;
|
||||
|
||||
if (!bch2_csum_type_is_encryption(op->crc.csum_type))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If we need to decrypt data in the write path, we'll no longer be able
|
||||
* to verify the existing checksum (poly1305 mac, in this case) after
|
||||
* it's decrypted - this is the last point we'll be able to reverify the
|
||||
* checksum:
|
||||
*/
|
||||
csum = bch2_checksum_bio(c, op->crc.csum_type, nonce, &op->wbio.bio);
|
||||
if (bch2_crc_cmp(op->crc.csum, csum) && !c->opts.no_data_io)
|
||||
return -EIO;
|
||||
|
||||
ret = bch2_encrypt_bio(c, op->crc.csum_type, nonce, &op->wbio.bio);
|
||||
op->crc.csum_type = 0;
|
||||
op->crc.csum = (struct bch_csum) { 0, 0 };
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum prep_encoded_ret {
|
||||
PREP_ENCODED_OK,
|
||||
PREP_ENCODED_ERR,
|
||||
PREP_ENCODED_CHECKSUM_ERR,
|
||||
PREP_ENCODED_DO_WRITE,
|
||||
} bch2_write_prep_encoded_data(struct bch_write_op *op, struct write_point *wp)
|
||||
static noinline int bch2_write_prep_encoded_data(struct bch_write_op *op, struct write_point *wp)
|
||||
{
|
||||
struct bch_fs *c = op->c;
|
||||
struct bio *bio = &op->wbio.bio;
|
||||
|
||||
if (!(op->flags & BCH_WRITE_data_encoded))
|
||||
return PREP_ENCODED_OK;
|
||||
struct bch_csum csum;
|
||||
int ret = 0;
|
||||
|
||||
BUG_ON(bio_sectors(bio) != op->crc.compressed_size);
|
||||
|
||||
@ -906,12 +845,13 @@ static enum prep_encoded_ret {
|
||||
(op->crc.compression_type == bch2_compression_opt_to_type(op->compression_opt) ||
|
||||
op->incompressible)) {
|
||||
if (!crc_is_compressed(op->crc) &&
|
||||
op->csum_type != op->crc.csum_type &&
|
||||
bch2_write_rechecksum(c, op, op->csum_type) &&
|
||||
!c->opts.no_data_io)
|
||||
return PREP_ENCODED_CHECKSUM_ERR;
|
||||
op->csum_type != op->crc.csum_type) {
|
||||
ret = bch2_write_rechecksum(c, op, op->csum_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return PREP_ENCODED_DO_WRITE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -919,20 +859,24 @@ static enum prep_encoded_ret {
|
||||
* is, we have to decompress it:
|
||||
*/
|
||||
if (crc_is_compressed(op->crc)) {
|
||||
struct bch_csum csum;
|
||||
|
||||
if (bch2_write_decrypt(op))
|
||||
return PREP_ENCODED_CHECKSUM_ERR;
|
||||
|
||||
/* Last point we can still verify checksum: */
|
||||
csum = bch2_checksum_bio(c, op->crc.csum_type,
|
||||
extent_nonce(op->version, op->crc),
|
||||
bio);
|
||||
struct nonce nonce = extent_nonce(op->version, op->crc);
|
||||
csum = bch2_checksum_bio(c, op->crc.csum_type, nonce, bio);
|
||||
if (bch2_crc_cmp(op->crc.csum, csum) && !c->opts.no_data_io)
|
||||
return PREP_ENCODED_CHECKSUM_ERR;
|
||||
goto csum_err;
|
||||
|
||||
if (bch2_bio_uncompress_inplace(op, bio))
|
||||
return PREP_ENCODED_ERR;
|
||||
if (bch2_csum_type_is_encryption(op->crc.csum_type)) {
|
||||
ret = bch2_encrypt_bio(c, op->crc.csum_type, nonce, bio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
op->crc.csum_type = 0;
|
||||
op->crc.csum = (struct bch_csum) { 0, 0 };
|
||||
}
|
||||
|
||||
ret = bch2_bio_uncompress_inplace(op, bio);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -944,22 +888,44 @@ static enum prep_encoded_ret {
|
||||
* If the data is checksummed and we're only writing a subset,
|
||||
* rechecksum and adjust bio to point to currently live data:
|
||||
*/
|
||||
if ((op->crc.live_size != op->crc.uncompressed_size ||
|
||||
op->crc.csum_type != op->csum_type) &&
|
||||
bch2_write_rechecksum(c, op, op->csum_type) &&
|
||||
!c->opts.no_data_io)
|
||||
return PREP_ENCODED_CHECKSUM_ERR;
|
||||
if (op->crc.live_size != op->crc.uncompressed_size ||
|
||||
op->crc.csum_type != op->csum_type) {
|
||||
ret = bch2_write_rechecksum(c, op, op->csum_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we want to compress the data, it has to be decrypted:
|
||||
*/
|
||||
if ((op->compression_opt ||
|
||||
bch2_csum_type_is_encryption(op->crc.csum_type) !=
|
||||
bch2_csum_type_is_encryption(op->csum_type)) &&
|
||||
bch2_write_decrypt(op))
|
||||
return PREP_ENCODED_CHECKSUM_ERR;
|
||||
if (bch2_csum_type_is_encryption(op->crc.csum_type) &&
|
||||
(op->compression_opt || op->crc.csum_type != op->csum_type)) {
|
||||
struct nonce nonce = extent_nonce(op->version, op->crc);
|
||||
csum = bch2_checksum_bio(c, op->crc.csum_type, nonce, bio);
|
||||
if (bch2_crc_cmp(op->crc.csum, csum) && !c->opts.no_data_io)
|
||||
goto csum_err;
|
||||
|
||||
return PREP_ENCODED_OK;
|
||||
ret = bch2_encrypt_bio(c, op->crc.csum_type, nonce, bio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
op->crc.csum_type = 0;
|
||||
op->crc.csum = (struct bch_csum) { 0, 0 };
|
||||
}
|
||||
|
||||
return 0;
|
||||
csum_err:
|
||||
bch2_write_op_error(op, op->pos.offset,
|
||||
"error verifying existing checksum while moving existing data (memory corruption?)\n"
|
||||
" expected %0llx:%0llx got %0llx:%0llx type %s",
|
||||
op->crc.csum.hi,
|
||||
op->crc.csum.lo,
|
||||
csum.hi,
|
||||
csum.lo,
|
||||
op->crc.csum_type < BCH_CSUM_NR
|
||||
? __bch2_csum_types[op->crc.csum_type]
|
||||
: "(unknown)");
|
||||
return -BCH_ERR_data_write_csum;
|
||||
}
|
||||
|
||||
static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
|
||||
@ -974,29 +940,28 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
|
||||
bool page_alloc_failed = false;
|
||||
int ret, more = 0;
|
||||
|
||||
if (op->incompressible)
|
||||
op->compression_opt = 0;
|
||||
|
||||
BUG_ON(!bio_sectors(src));
|
||||
|
||||
ec_buf = bch2_writepoint_ec_buf(c, wp);
|
||||
|
||||
switch (bch2_write_prep_encoded_data(op, wp)) {
|
||||
case PREP_ENCODED_OK:
|
||||
break;
|
||||
case PREP_ENCODED_ERR:
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
case PREP_ENCODED_CHECKSUM_ERR:
|
||||
goto csum_err;
|
||||
case PREP_ENCODED_DO_WRITE:
|
||||
/* XXX look for bug here */
|
||||
if (ec_buf) {
|
||||
dst = bch2_write_bio_alloc(c, wp, src,
|
||||
&page_alloc_failed,
|
||||
ec_buf);
|
||||
bio_copy_data(dst, src);
|
||||
bounce = true;
|
||||
if (unlikely(op->flags & BCH_WRITE_data_encoded)) {
|
||||
ret = bch2_write_prep_encoded_data(op, wp);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
if (ret) {
|
||||
if (ec_buf) {
|
||||
dst = bch2_write_bio_alloc(c, wp, src,
|
||||
&page_alloc_failed,
|
||||
ec_buf);
|
||||
bio_copy_data(dst, src);
|
||||
bounce = true;
|
||||
}
|
||||
init_append_extent(op, wp, op->version, op->crc);
|
||||
goto do_write;
|
||||
}
|
||||
init_append_extent(op, wp, op->version, op->crc);
|
||||
goto do_write;
|
||||
}
|
||||
|
||||
if (ec_buf ||
|
||||
@ -1089,12 +1054,13 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
|
||||
* data can't be modified (by userspace) while it's in
|
||||
* flight.
|
||||
*/
|
||||
if (bch2_rechecksum_bio(c, src, version, op->crc,
|
||||
ret = bch2_rechecksum_bio(c, src, version, op->crc,
|
||||
&crc, &op->crc,
|
||||
src_len >> 9,
|
||||
bio_sectors(src) - (src_len >> 9),
|
||||
op->csum_type))
|
||||
goto csum_err;
|
||||
op->csum_type);
|
||||
if (ret)
|
||||
goto err;
|
||||
/*
|
||||
* rchecksum_bio sets compression_type on crc from op->crc,
|
||||
* this isn't always correct as sometimes we're changing
|
||||
@ -1104,12 +1070,12 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
|
||||
crc.nonce = nonce;
|
||||
} else {
|
||||
if ((op->flags & BCH_WRITE_data_encoded) &&
|
||||
bch2_rechecksum_bio(c, src, version, op->crc,
|
||||
(ret = bch2_rechecksum_bio(c, src, version, op->crc,
|
||||
NULL, &op->crc,
|
||||
src_len >> 9,
|
||||
bio_sectors(src) - (src_len >> 9),
|
||||
op->crc.csum_type))
|
||||
goto csum_err;
|
||||
op->crc.csum_type)))
|
||||
goto err;
|
||||
|
||||
crc.compressed_size = dst_len >> 9;
|
||||
crc.uncompressed_size = src_len >> 9;
|
||||
@ -1168,16 +1134,6 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
|
||||
do_write:
|
||||
*_dst = dst;
|
||||
return more;
|
||||
csum_err:
|
||||
{
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_write_op_error(&buf, op, op->pos.offset,
|
||||
"error verifying existing checksum while rewriting existing data (memory corruption?)");
|
||||
bch_err_ratelimited(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
|
||||
ret = -EIO;
|
||||
err:
|
||||
if (to_wbio(dst)->bounce)
|
||||
bch2_bio_free_pages_pool(c, dst);
|
||||
@ -1255,38 +1211,35 @@ static void bch2_nocow_write_convert_unwritten(struct bch_write_op *op)
|
||||
{
|
||||
struct bch_fs *c = op->c;
|
||||
struct btree_trans *trans = bch2_trans_get(c);
|
||||
int ret = 0;
|
||||
|
||||
for_each_keylist_key(&op->insert_keys, orig) {
|
||||
int ret = for_each_btree_key_max_commit(trans, iter, BTREE_ID_extents,
|
||||
ret = for_each_btree_key_max_commit(trans, iter, BTREE_ID_extents,
|
||||
bkey_start_pos(&orig->k), orig->k.p,
|
||||
BTREE_ITER_intent, k,
|
||||
NULL, NULL, BCH_TRANS_COMMIT_no_enospc, ({
|
||||
bch2_nocow_write_convert_one_unwritten(trans, &iter, orig, k, op->new_i_size);
|
||||
}));
|
||||
|
||||
if (ret && !bch2_err_matches(ret, EROFS)) {
|
||||
struct bkey_i *insert = bch2_keylist_front(&op->insert_keys);
|
||||
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_write_op_error_trans(trans, &buf, op, bkey_start_offset(&insert->k),
|
||||
"btree update error: %s", bch2_err_str(ret));
|
||||
bch_err_ratelimited(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
op->error = ret;
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bch2_trans_put(trans);
|
||||
|
||||
if (ret && !bch2_err_matches(ret, EROFS)) {
|
||||
struct bkey_i *insert = bch2_keylist_front(&op->insert_keys);
|
||||
bch2_write_op_error(op, bkey_start_offset(&insert->k),
|
||||
"btree update error: %s", bch2_err_str(ret));
|
||||
}
|
||||
|
||||
if (ret)
|
||||
op->error = ret;
|
||||
}
|
||||
|
||||
static void __bch2_nocow_write_done(struct bch_write_op *op)
|
||||
{
|
||||
if (unlikely(op->flags & BCH_WRITE_io_error)) {
|
||||
op->error = -EIO;
|
||||
op->error = -BCH_ERR_data_write_io;
|
||||
} else if (unlikely(op->flags & BCH_WRITE_convert_unwritten))
|
||||
bch2_nocow_write_convert_unwritten(op);
|
||||
}
|
||||
@ -1436,11 +1389,8 @@ err:
|
||||
darray_exit(&buckets);
|
||||
|
||||
if (ret) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_write_op_error(&buf, op, op->pos.offset,
|
||||
bch2_write_op_error(op, op->pos.offset,
|
||||
"%s(): btree lookup error: %s", __func__, bch2_err_str(ret));
|
||||
bch_err_ratelimited(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
op->error = ret;
|
||||
op->flags |= BCH_WRITE_submitted;
|
||||
}
|
||||
@ -1480,7 +1430,7 @@ err_bucket_stale:
|
||||
"pointer to invalid bucket in nocow path on device %llu\n %s",
|
||||
stale_at->b.inode,
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
ret = -EIO;
|
||||
ret = -BCH_ERR_data_write_invalid_ptr;
|
||||
} else {
|
||||
/* We can retry this: */
|
||||
ret = -BCH_ERR_transaction_restart;
|
||||
@ -1558,13 +1508,9 @@ err:
|
||||
op->flags |= BCH_WRITE_submitted;
|
||||
|
||||
if (unlikely(ret < 0)) {
|
||||
if (!(op->flags & BCH_WRITE_alloc_nowait)) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_write_op_error(&buf, op, op->pos.offset,
|
||||
if (!(op->flags & BCH_WRITE_alloc_nowait))
|
||||
bch2_write_op_error(op, op->pos.offset,
|
||||
"%s(): %s", __func__, bch2_err_str(ret));
|
||||
bch_err_ratelimited(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
op->error = ret;
|
||||
break;
|
||||
}
|
||||
@ -1691,11 +1637,8 @@ CLOSURE_CALLBACK(bch2_write)
|
||||
wbio_init(bio)->put_bio = false;
|
||||
|
||||
if (unlikely(bio->bi_iter.bi_size & (c->opts.block_size - 1))) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_write_op_error(&buf, op, op->pos.offset,
|
||||
"misaligned write");
|
||||
printbuf_exit(&buf);
|
||||
op->error = -EIO;
|
||||
bch2_write_op_error(op, op->pos.offset, "misaligned write");
|
||||
op->error = -BCH_ERR_data_write_misaligned;
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -14,13 +14,8 @@ void bch2_bio_alloc_pages_pool(struct bch_fs *, struct bio *, size_t);
|
||||
void bch2_submit_wbio_replicas(struct bch_write_bio *, struct bch_fs *,
|
||||
enum bch_data_type, const struct bkey_i *, bool);
|
||||
|
||||
__printf(5, 6)
|
||||
void bch2_write_op_error_trans(struct btree_trans *trans, struct printbuf *out,
|
||||
struct bch_write_op *op, u64, const char *, ...);
|
||||
|
||||
__printf(4, 5)
|
||||
void bch2_write_op_error(struct printbuf *out, struct bch_write_op *op, u64,
|
||||
const char *, ...);
|
||||
__printf(3, 4)
|
||||
void bch2_write_op_error(struct bch_write_op *op, u64, const char *, ...);
|
||||
|
||||
#define BCH_WRITE_FLAGS() \
|
||||
x(alloc_nowait) \
|
||||
|
@ -62,8 +62,7 @@ static void bch2_journal_buf_to_text(struct printbuf *out, struct journal *j, u6
|
||||
prt_newline(out);
|
||||
}
|
||||
|
||||
prt_printf(out, "expires:\t");
|
||||
prt_printf(out, "%li jiffies\n", buf->expires - jiffies);
|
||||
prt_printf(out, "expires:\t%li jiffies\n", buf->expires - jiffies);
|
||||
|
||||
prt_printf(out, "flags:\t");
|
||||
if (buf->noflush)
|
||||
@ -142,6 +141,8 @@ journal_error_check_stuck(struct journal *j, int error, unsigned flags)
|
||||
bool stuck = false;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
|
||||
buf.atomic++;
|
||||
|
||||
if (!(error == -BCH_ERR_journal_full ||
|
||||
error == -BCH_ERR_journal_pin_full) ||
|
||||
nr_unwritten_journal_entries(j) ||
|
||||
@ -167,12 +168,12 @@ journal_error_check_stuck(struct journal *j, int error, unsigned flags)
|
||||
return stuck;
|
||||
}
|
||||
j->err_seq = journal_cur_seq(j);
|
||||
spin_unlock(&j->lock);
|
||||
|
||||
bch_err(c, "Journal stuck! Hava a pre-reservation but journal full (error %s)",
|
||||
bch2_err_str(error));
|
||||
bch2_journal_debug_to_text(&buf, j);
|
||||
bch_err(c, "%s", buf.buf);
|
||||
__bch2_journal_debug_to_text(&buf, j);
|
||||
spin_unlock(&j->lock);
|
||||
prt_printf(&buf, bch2_fmt(c, "Journal stuck! Hava a pre-reservation but journal full (error %s)"),
|
||||
bch2_err_str(error));
|
||||
bch2_print_string_as_lines(KERN_ERR, buf.buf);
|
||||
|
||||
printbuf_reset(&buf);
|
||||
bch2_journal_pins_to_text(&buf, j);
|
||||
@ -728,8 +729,8 @@ int bch2_journal_res_get_slowpath(struct journal *j, struct journal_res *res,
|
||||
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_journal_debug_to_text(&buf, j);
|
||||
bch_err(c, "Journal stuck? Waited for 10 seconds...\n%s",
|
||||
buf.buf);
|
||||
bch2_print_string_as_lines(KERN_ERR, buf.buf);
|
||||
prt_printf(&buf, bch2_fmt(c, "Journal stuck? Waited for 10 seconds, err %s"), bch2_err_str(ret));
|
||||
printbuf_exit(&buf);
|
||||
|
||||
closure_wait_event(&j->async_wait,
|
||||
@ -1510,7 +1511,7 @@ int bch2_dev_journal_init(struct bch_dev *ca, struct bch_sb *sb)
|
||||
unsigned nr_bvecs = DIV_ROUND_UP(JOURNAL_ENTRY_SIZE_MAX, PAGE_SIZE);
|
||||
|
||||
for (unsigned i = 0; i < ARRAY_SIZE(ja->bio); i++) {
|
||||
ja->bio[i] = kmalloc(struct_size(ja->bio[i], bio.bi_inline_vecs,
|
||||
ja->bio[i] = kzalloc(struct_size(ja->bio[i], bio.bi_inline_vecs,
|
||||
nr_bvecs), GFP_KERNEL);
|
||||
if (!ja->bio[i])
|
||||
return -BCH_ERR_ENOMEM_dev_journal_init;
|
||||
|
@ -214,12 +214,12 @@ static int journal_entry_add(struct bch_fs *c, struct bch_dev *ca,
|
||||
|
||||
fsck_err_on(same_device,
|
||||
c, journal_entry_dup_same_device,
|
||||
"duplicate journal entry on same device\n %s",
|
||||
"duplicate journal entry on same device\n%s",
|
||||
buf.buf);
|
||||
|
||||
fsck_err_on(not_identical,
|
||||
c, journal_entry_replicas_data_mismatch,
|
||||
"found duplicate but non identical journal entries\n %s",
|
||||
"found duplicate but non identical journal entries\n%s",
|
||||
buf.buf);
|
||||
|
||||
if (entry_ptr.csum_good && !identical)
|
||||
@ -308,8 +308,8 @@ static void journal_entry_err_msg(struct printbuf *out,
|
||||
break; \
|
||||
case WRITE: \
|
||||
bch2_sb_error_count(c, BCH_FSCK_ERR_##_err); \
|
||||
bch_err(c, "corrupt metadata before write: %s\n", _buf.buf);\
|
||||
if (bch2_fs_inconsistent(c)) { \
|
||||
if (bch2_fs_inconsistent(c, \
|
||||
"corrupt metadata before write: %s\n", _buf.buf)) {\
|
||||
ret = -BCH_ERR_fsck_errors_not_fixed; \
|
||||
goto fsck_err; \
|
||||
} \
|
||||
@ -764,6 +764,23 @@ static void journal_entry_overwrite_to_text(struct printbuf *out, struct bch_fs
|
||||
journal_entry_btree_keys_to_text(out, c, entry);
|
||||
}
|
||||
|
||||
static int journal_entry_log_bkey_validate(struct bch_fs *c,
|
||||
struct jset *jset,
|
||||
struct jset_entry *entry,
|
||||
unsigned version, int big_endian,
|
||||
struct bkey_validate_context from)
|
||||
{
|
||||
from.flags = 0;
|
||||
return journal_entry_btree_keys_validate(c, jset, entry,
|
||||
version, big_endian, from);
|
||||
}
|
||||
|
||||
static void journal_entry_log_bkey_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
struct jset_entry *entry)
|
||||
{
|
||||
journal_entry_btree_keys_to_text(out, c, entry);
|
||||
}
|
||||
|
||||
static int journal_entry_write_buffer_keys_validate(struct bch_fs *c,
|
||||
struct jset *jset,
|
||||
struct jset_entry *entry,
|
||||
@ -1371,8 +1388,8 @@ int bch2_journal_read(struct bch_fs *c,
|
||||
missing_end = seq - 1;
|
||||
fsck_err(c, journal_entries_missing,
|
||||
"journal entries %llu-%llu missing! (replaying %llu-%llu)\n"
|
||||
" prev at %s\n"
|
||||
" next at %s, continue?",
|
||||
"prev at %s\n"
|
||||
"next at %s, continue?",
|
||||
missing_start, missing_end,
|
||||
*last_seq, *blacklist_seq - 1,
|
||||
buf1.buf, buf2.buf);
|
||||
@ -1426,7 +1443,7 @@ int bch2_journal_read(struct bch_fs *c,
|
||||
!bch2_replicas_marked(c, &replicas.e) &&
|
||||
(le64_to_cpu(i->j.seq) == *last_seq ||
|
||||
fsck_err(c, journal_entry_replicas_not_marked,
|
||||
"superblock not marked as containing replicas for journal entry %llu\n %s",
|
||||
"superblock not marked as containing replicas for journal entry %llu\n%s",
|
||||
le64_to_cpu(i->j.seq), buf.buf))) {
|
||||
ret = bch2_mark_replicas(c, &replicas.e);
|
||||
if (ret)
|
||||
@ -1623,8 +1640,9 @@ static CLOSURE_CALLBACK(journal_write_done)
|
||||
: j->noflush_write_time, j->write_start_time);
|
||||
|
||||
if (!w->devs_written.nr) {
|
||||
bch_err(c, "unable to write journal to sufficient devices");
|
||||
err = -EIO;
|
||||
if (!bch2_journal_error(j))
|
||||
bch_err(c, "unable to write journal to sufficient devices");
|
||||
err = -BCH_ERR_journal_write_err;
|
||||
} else {
|
||||
bch2_devlist_to_replicas(&replicas.e, BCH_DATA_journal,
|
||||
w->devs_written);
|
||||
@ -2081,12 +2099,12 @@ CLOSURE_CALLBACK(bch2_journal_write)
|
||||
struct printbuf buf = PRINTBUF;
|
||||
buf.atomic++;
|
||||
|
||||
__bch2_journal_debug_to_text(&buf, j);
|
||||
spin_unlock(&j->lock);
|
||||
prt_printf(&buf, bch2_fmt(c, "Unable to allocate journal write at seq %llu for %zu sectors: %s"),
|
||||
le64_to_cpu(w->data->seq),
|
||||
vstruct_sectors(w->data, c->block_bits),
|
||||
bch2_err_str(ret));
|
||||
__bch2_journal_debug_to_text(&buf, j);
|
||||
spin_unlock(&j->lock);
|
||||
bch2_print_string_as_lines(KERN_ERR, buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
|
@ -645,7 +645,6 @@ static u64 journal_seq_to_flush(struct journal *j)
|
||||
* @j: journal object
|
||||
* @direct: direct or background reclaim?
|
||||
* @kicked: requested to run since we last ran?
|
||||
* Returns: 0 on success, or -EIO if the journal has been shutdown
|
||||
*
|
||||
* Background journal reclaim writes out btree nodes. It should be run
|
||||
* early enough so that we never completely run out of journal buckets.
|
||||
@ -685,10 +684,9 @@ static int __bch2_journal_reclaim(struct journal *j, bool direct, bool kicked)
|
||||
if (kthread && kthread_should_stop())
|
||||
break;
|
||||
|
||||
if (bch2_journal_error(j)) {
|
||||
ret = -EIO;
|
||||
ret = bch2_journal_error(j);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
bch2_journal_do_discards(j);
|
||||
|
||||
|
@ -101,8 +101,7 @@ int bch2_lru_check_set(struct btree_trans *trans,
|
||||
goto err;
|
||||
|
||||
if (fsck_err(trans, alloc_key_to_missing_lru_entry,
|
||||
"missing %s lru entry\n"
|
||||
" %s",
|
||||
"missing %s lru entry\n%s",
|
||||
bch2_lru_types[lru_type(lru_k)],
|
||||
(bch2_bkey_val_to_text(&buf, c, referring_k), buf.buf))) {
|
||||
ret = bch2_lru_set(trans, lru_id, dev_bucket, time);
|
||||
@ -190,8 +189,8 @@ static int bch2_check_lru_key(struct btree_trans *trans,
|
||||
|
||||
if (fsck_err(trans, lru_entry_bad,
|
||||
"incorrect lru entry: lru %s time %llu\n"
|
||||
" %s\n"
|
||||
" for %s",
|
||||
"%s\n"
|
||||
"for %s",
|
||||
bch2_lru_types[type],
|
||||
lru_pos_time(lru_k.k->p),
|
||||
(bch2_bkey_val_to_text(&buf1, c, lru_k), buf1.buf),
|
||||
|
@ -528,6 +528,37 @@ int bch2_move_ratelimit(struct moving_context *ctxt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move requires non extents iterators, and there's also no need for it to
|
||||
* signal indirect_extent_missing_error:
|
||||
*/
|
||||
static struct bkey_s_c bch2_lookup_indirect_extent_for_move(struct btree_trans *trans,
|
||||
struct btree_iter *iter,
|
||||
struct bkey_s_c_reflink_p p)
|
||||
{
|
||||
if (unlikely(REFLINK_P_ERROR(p.v)))
|
||||
return bkey_s_c_null;
|
||||
|
||||
struct bpos reflink_pos = POS(0, REFLINK_P_IDX(p.v));
|
||||
|
||||
bch2_trans_iter_init(trans, iter,
|
||||
BTREE_ID_reflink, reflink_pos,
|
||||
BTREE_ITER_not_extents);
|
||||
|
||||
struct bkey_s_c k = bch2_btree_iter_peek(iter);
|
||||
if (!k.k || bkey_err(k)) {
|
||||
bch2_trans_iter_exit(trans, iter);
|
||||
return k;
|
||||
}
|
||||
|
||||
if (bkey_lt(reflink_pos, bkey_start_pos(k.k))) {
|
||||
bch2_trans_iter_exit(trans, iter);
|
||||
return bkey_s_c_null;
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
static int bch2_move_data_btree(struct moving_context *ctxt,
|
||||
struct bpos start,
|
||||
struct bpos end,
|
||||
@ -592,17 +623,16 @@ static int bch2_move_data_btree(struct moving_context *ctxt,
|
||||
k.k->type == KEY_TYPE_reflink_p &&
|
||||
REFLINK_P_MAY_UPDATE_OPTIONS(bkey_s_c_to_reflink_p(k).v)) {
|
||||
struct bkey_s_c_reflink_p p = bkey_s_c_to_reflink_p(k);
|
||||
s64 offset_into_extent = 0;
|
||||
|
||||
bch2_trans_iter_exit(trans, &reflink_iter);
|
||||
k = bch2_lookup_indirect_extent(trans, &reflink_iter, &offset_into_extent, p, true, 0);
|
||||
k = bch2_lookup_indirect_extent_for_move(trans, &reflink_iter, p);
|
||||
ret = bkey_err(k);
|
||||
if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
|
||||
continue;
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (bkey_deleted(k.k))
|
||||
if (!k.k)
|
||||
goto next_nondata;
|
||||
|
||||
/*
|
||||
@ -611,7 +641,6 @@ static int bch2_move_data_btree(struct moving_context *ctxt,
|
||||
* pointer - need to fixup iter->k
|
||||
*/
|
||||
extent_iter = &reflink_iter;
|
||||
offset_into_extent = 0;
|
||||
}
|
||||
|
||||
if (!bkey_extent_is_direct_data(k.k))
|
||||
|
@ -32,7 +32,7 @@ struct bch_move_stats {
|
||||
|
||||
struct move_bucket_key {
|
||||
struct bpos bucket;
|
||||
u8 gen;
|
||||
unsigned gen;
|
||||
};
|
||||
|
||||
struct move_bucket {
|
||||
|
@ -4,8 +4,8 @@
|
||||
#include "acl.h"
|
||||
#include "btree_update.h"
|
||||
#include "dirent.h"
|
||||
#include "fs-common.h"
|
||||
#include "inode.h"
|
||||
#include "namei.h"
|
||||
#include "subvolume.h"
|
||||
#include "xattr.h"
|
||||
|
||||
@ -564,6 +564,8 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* inum_to_path */
|
||||
|
||||
static inline void prt_bytes_reversed(struct printbuf *out, const void *b, unsigned n)
|
||||
{
|
||||
bch2_printbuf_make_room(out, n);
|
||||
@ -654,3 +656,179 @@ disconnected:
|
||||
prt_str_reversed(path, "(disconnected)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* fsck */
|
||||
|
||||
static int bch2_check_dirent_inode_dirent(struct btree_trans *trans,
|
||||
struct bkey_s_c_dirent d,
|
||||
struct bch_inode_unpacked *target,
|
||||
bool in_fsck)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
struct btree_iter bp_iter = { NULL };
|
||||
int ret = 0;
|
||||
|
||||
if (inode_points_to_dirent(target, d))
|
||||
return 0;
|
||||
|
||||
if (!target->bi_dir &&
|
||||
!target->bi_dir_offset) {
|
||||
fsck_err_on(S_ISDIR(target->bi_mode),
|
||||
trans, inode_dir_missing_backpointer,
|
||||
"directory with missing backpointer\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, d.s_c),
|
||||
prt_printf(&buf, "\n"),
|
||||
bch2_inode_unpacked_to_text(&buf, target),
|
||||
buf.buf));
|
||||
|
||||
fsck_err_on(target->bi_flags & BCH_INODE_unlinked,
|
||||
trans, inode_unlinked_but_has_dirent,
|
||||
"inode unlinked but has dirent\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, d.s_c),
|
||||
prt_printf(&buf, "\n"),
|
||||
bch2_inode_unpacked_to_text(&buf, target),
|
||||
buf.buf));
|
||||
|
||||
target->bi_flags &= ~BCH_INODE_unlinked;
|
||||
target->bi_dir = d.k->p.inode;
|
||||
target->bi_dir_offset = d.k->p.offset;
|
||||
return __bch2_fsck_write_inode(trans, target);
|
||||
}
|
||||
|
||||
if (bch2_inode_should_have_single_bp(target) &&
|
||||
!fsck_err(trans, inode_wrong_backpointer,
|
||||
"dirent points to inode that does not point back:\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, d.s_c),
|
||||
prt_newline(&buf),
|
||||
bch2_inode_unpacked_to_text(&buf, target),
|
||||
buf.buf)))
|
||||
goto err;
|
||||
|
||||
struct bkey_s_c_dirent bp_dirent =
|
||||
bch2_bkey_get_iter_typed(trans, &bp_iter, BTREE_ID_dirents,
|
||||
SPOS(target->bi_dir, target->bi_dir_offset, target->bi_snapshot),
|
||||
0, dirent);
|
||||
ret = bkey_err(bp_dirent);
|
||||
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||
goto err;
|
||||
|
||||
bool backpointer_exists = !ret;
|
||||
ret = 0;
|
||||
|
||||
if (!backpointer_exists) {
|
||||
if (fsck_err(trans, inode_wrong_backpointer,
|
||||
"inode %llu:%u has wrong backpointer:\n"
|
||||
"got %llu:%llu\n"
|
||||
"should be %llu:%llu",
|
||||
target->bi_inum, target->bi_snapshot,
|
||||
target->bi_dir,
|
||||
target->bi_dir_offset,
|
||||
d.k->p.inode,
|
||||
d.k->p.offset)) {
|
||||
target->bi_dir = d.k->p.inode;
|
||||
target->bi_dir_offset = d.k->p.offset;
|
||||
ret = __bch2_fsck_write_inode(trans, target);
|
||||
}
|
||||
} else {
|
||||
bch2_bkey_val_to_text(&buf, c, d.s_c);
|
||||
prt_newline(&buf);
|
||||
bch2_bkey_val_to_text(&buf, c, bp_dirent.s_c);
|
||||
|
||||
if (S_ISDIR(target->bi_mode) || target->bi_subvol) {
|
||||
/*
|
||||
* XXX: verify connectivity of the other dirent
|
||||
* up to the root before removing this one
|
||||
*
|
||||
* Additionally, bch2_lookup would need to cope with the
|
||||
* dirent it found being removed - or should we remove
|
||||
* the other one, even though the inode points to it?
|
||||
*/
|
||||
if (in_fsck) {
|
||||
if (fsck_err(trans, inode_dir_multiple_links,
|
||||
"%s %llu:%u with multiple links\n%s",
|
||||
S_ISDIR(target->bi_mode) ? "directory" : "subvolume",
|
||||
target->bi_inum, target->bi_snapshot, buf.buf))
|
||||
ret = bch2_fsck_remove_dirent(trans, d.k->p);
|
||||
} else {
|
||||
bch2_fs_inconsistent(c,
|
||||
"%s %llu:%u with multiple links\n%s",
|
||||
S_ISDIR(target->bi_mode) ? "directory" : "subvolume",
|
||||
target->bi_inum, target->bi_snapshot, buf.buf);
|
||||
}
|
||||
|
||||
goto out;
|
||||
} else {
|
||||
/*
|
||||
* hardlinked file with nlink 0:
|
||||
* We're just adjusting nlink here so check_nlinks() will pick
|
||||
* it up, it ignores inodes with nlink 0
|
||||
*/
|
||||
if (fsck_err_on(!target->bi_nlink,
|
||||
trans, inode_multiple_links_but_nlink_0,
|
||||
"inode %llu:%u type %s has multiple links but i_nlink 0\n%s",
|
||||
target->bi_inum, target->bi_snapshot, bch2_d_types[d.v->d_type], buf.buf)) {
|
||||
target->bi_nlink++;
|
||||
target->bi_flags &= ~BCH_INODE_unlinked;
|
||||
ret = __bch2_fsck_write_inode(trans, target);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
err:
|
||||
fsck_err:
|
||||
bch2_trans_iter_exit(trans, &bp_iter);
|
||||
printbuf_exit(&buf);
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __bch2_check_dirent_target(struct btree_trans *trans,
|
||||
struct btree_iter *dirent_iter,
|
||||
struct bkey_s_c_dirent d,
|
||||
struct bch_inode_unpacked *target,
|
||||
bool in_fsck)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
int ret = 0;
|
||||
|
||||
ret = bch2_check_dirent_inode_dirent(trans, d, target, in_fsck);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (fsck_err_on(d.v->d_type != inode_d_type(target),
|
||||
trans, dirent_d_type_wrong,
|
||||
"incorrect d_type: got %s, should be %s:\n%s",
|
||||
bch2_d_type_str(d.v->d_type),
|
||||
bch2_d_type_str(inode_d_type(target)),
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf))) {
|
||||
struct bkey_i_dirent *n = bch2_trans_kmalloc(trans, bkey_bytes(d.k));
|
||||
ret = PTR_ERR_OR_ZERO(n);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
bkey_reassemble(&n->k_i, d.s_c);
|
||||
n->v.d_type = inode_d_type(target);
|
||||
if (n->v.d_type == DT_SUBVOL) {
|
||||
n->v.d_parent_subvol = cpu_to_le32(target->bi_parent_subvol);
|
||||
n->v.d_child_subvol = cpu_to_le32(target->bi_subvol);
|
||||
} else {
|
||||
n->v.d_inum = cpu_to_le64(target->bi_inum);
|
||||
}
|
||||
|
||||
ret = bch2_trans_update(trans, dirent_iter, &n->k_i, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
err:
|
||||
fsck_err:
|
||||
printbuf_exit(&buf);
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _BCACHEFS_FS_COMMON_H
|
||||
#define _BCACHEFS_FS_COMMON_H
|
||||
#ifndef _BCACHEFS_NAMEI_H
|
||||
#define _BCACHEFS_NAMEI_H
|
||||
|
||||
#include "dirent.h"
|
||||
|
||||
@ -44,4 +44,29 @@ bool bch2_reinherit_attrs(struct bch_inode_unpacked *,
|
||||
|
||||
int bch2_inum_to_path(struct btree_trans *, subvol_inum, struct printbuf *);
|
||||
|
||||
#endif /* _BCACHEFS_FS_COMMON_H */
|
||||
int __bch2_check_dirent_target(struct btree_trans *,
|
||||
struct btree_iter *,
|
||||
struct bkey_s_c_dirent,
|
||||
struct bch_inode_unpacked *, bool);
|
||||
|
||||
static inline bool inode_points_to_dirent(struct bch_inode_unpacked *inode,
|
||||
struct bkey_s_c_dirent d)
|
||||
{
|
||||
return inode->bi_dir == d.k->p.inode &&
|
||||
inode->bi_dir_offset == d.k->p.offset;
|
||||
}
|
||||
|
||||
static inline int bch2_check_dirent_target(struct btree_trans *trans,
|
||||
struct btree_iter *dirent_iter,
|
||||
struct bkey_s_c_dirent d,
|
||||
struct bch_inode_unpacked *target,
|
||||
bool in_fsck)
|
||||
{
|
||||
if (likely(inode_points_to_dirent(target, d) &&
|
||||
d.v->d_type == inode_d_type(target)))
|
||||
return 0;
|
||||
|
||||
return __bch2_check_dirent_target(trans, dirent_iter, d, target, in_fsck);
|
||||
}
|
||||
|
||||
#endif /* _BCACHEFS_NAMEI_H */
|
@ -44,7 +44,7 @@ const char * const __bch2_btree_ids[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char * const __bch2_csum_types[] = {
|
||||
const char * const __bch2_csum_types[] = {
|
||||
BCH_CSUM_TYPES()
|
||||
NULL
|
||||
};
|
||||
@ -219,10 +219,10 @@ typedef void (*sb_opt_set_fn)(struct bch_sb *, u64);
|
||||
typedef u64 (*member_opt_get_fn)(const struct bch_member *);
|
||||
typedef void (*member_opt_set_fn)(struct bch_member *, u64);
|
||||
|
||||
static const sb_opt_get_fn BCH2_NO_SB_OPT = NULL;
|
||||
static const sb_opt_set_fn SET_BCH2_NO_SB_OPT = NULL;
|
||||
static const member_opt_get_fn BCH2_NO_MEMBER_OPT = NULL;
|
||||
static const member_opt_set_fn SET_BCH2_NO_MEMBER_OPT = NULL;
|
||||
__maybe_unused static const sb_opt_get_fn BCH2_NO_SB_OPT = NULL;
|
||||
__maybe_unused static const sb_opt_set_fn SET_BCH2_NO_SB_OPT = NULL;
|
||||
__maybe_unused static const member_opt_get_fn BCH2_NO_MEMBER_OPT = NULL;
|
||||
__maybe_unused static const member_opt_set_fn SET_BCH2_NO_MEMBER_OPT = NULL;
|
||||
|
||||
#define type_compatible_or_null(_p, _type) \
|
||||
__builtin_choose_expr( \
|
||||
@ -482,14 +482,12 @@ void bch2_opts_to_text(struct printbuf *out,
|
||||
|
||||
int bch2_opt_check_may_set(struct bch_fs *c, struct bch_dev *ca, int id, u64 v)
|
||||
{
|
||||
lockdep_assert_held(&c->state_lock);
|
||||
|
||||
int ret = 0;
|
||||
|
||||
switch (id) {
|
||||
case Opt_state:
|
||||
if (ca)
|
||||
return __bch2_dev_set_state(c, ca, v, BCH_FORCE_IF_DEGRADED);
|
||||
return bch2_dev_set_state(c, ca, v, BCH_FORCE_IF_DEGRADED);
|
||||
break;
|
||||
|
||||
case Opt_compression:
|
||||
@ -551,14 +549,15 @@ int bch2_parse_one_mount_opt(struct bch_fs *c, struct bch_opts *opts,
|
||||
goto bad_opt;
|
||||
|
||||
ret = bch2_opt_parse(c, &bch2_opt_table[id], val, &v, &err);
|
||||
if (ret == -BCH_ERR_option_needs_open_fs && parse_later) {
|
||||
prt_printf(parse_later, "%s=%s,", name, val);
|
||||
if (parse_later->allocation_failure) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
if (ret == -BCH_ERR_option_needs_open_fs) {
|
||||
ret = 0;
|
||||
|
||||
if (parse_later) {
|
||||
prt_printf(parse_later, "%s=%s,", name, val);
|
||||
if (parse_later->allocation_failure)
|
||||
ret = -ENOMEM;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -569,28 +568,24 @@ int bch2_parse_one_mount_opt(struct bch_fs *c, struct bch_opts *opts,
|
||||
bch2_opt_set_by_id(opts, id, v);
|
||||
|
||||
ret = 0;
|
||||
goto out;
|
||||
|
||||
bad_opt:
|
||||
pr_err("Bad mount option %s", name);
|
||||
ret = -BCH_ERR_option_name;
|
||||
goto out;
|
||||
|
||||
bad_val:
|
||||
pr_err("Invalid mount option %s", err.buf);
|
||||
ret = -BCH_ERR_option_value;
|
||||
|
||||
out:
|
||||
printbuf_exit(&err);
|
||||
return ret;
|
||||
bad_opt:
|
||||
ret = -BCH_ERR_option_name;
|
||||
goto out;
|
||||
bad_val:
|
||||
ret = -BCH_ERR_option_value;
|
||||
goto out;
|
||||
}
|
||||
|
||||
int bch2_parse_mount_opts(struct bch_fs *c, struct bch_opts *opts,
|
||||
struct printbuf *parse_later, char *options)
|
||||
struct printbuf *parse_later, char *options,
|
||||
bool ignore_unknown)
|
||||
{
|
||||
char *copied_opts, *copied_opts_start;
|
||||
char *opt, *name, *val;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
@ -615,14 +610,14 @@ int bch2_parse_mount_opts(struct bch_fs *c, struct bch_opts *opts,
|
||||
val = opt;
|
||||
|
||||
ret = bch2_parse_one_mount_opt(c, opts, parse_later, name, val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret == -BCH_ERR_option_name && ignore_unknown)
|
||||
ret = 0;
|
||||
if (ret) {
|
||||
pr_err("Error parsing option %s: %s", name, bch2_err_str(ret));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto out;
|
||||
|
||||
out:
|
||||
kfree(copied_opts_start);
|
||||
return ret;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ extern const char * const bch2_version_upgrade_opts[];
|
||||
extern const char * const bch2_sb_features[];
|
||||
extern const char * const bch2_sb_compat[];
|
||||
extern const char * const __bch2_btree_ids[];
|
||||
extern const char * const __bch2_csum_types[];
|
||||
extern const char * const __bch2_csum_opts[];
|
||||
extern const char * const __bch2_compression_types[];
|
||||
extern const char * const bch2_compression_opts[];
|
||||
@ -499,11 +500,6 @@ enum fsck_err_opts {
|
||||
OPT_STR(bch2_member_states), \
|
||||
BCH_MEMBER_STATE, BCH_MEMBER_STATE_rw, \
|
||||
"state", "rw,ro,failed,spare") \
|
||||
x(fs_size, u64, \
|
||||
OPT_DEVICE|OPT_HIDDEN, \
|
||||
OPT_UINT(0, S64_MAX), \
|
||||
BCH2_NO_MEMBER_OPT, 0, \
|
||||
"size", "Size of filesystem on device") \
|
||||
x(bucket_size, u32, \
|
||||
OPT_DEVICE|OPT_HUMAN_READABLE|OPT_SB_FIELD_SECTORS, \
|
||||
OPT_UINT(0, S64_MAX), \
|
||||
@ -640,7 +636,7 @@ int bch2_opts_check_may_set(struct bch_fs *);
|
||||
int bch2_parse_one_mount_opt(struct bch_fs *, struct bch_opts *,
|
||||
struct printbuf *, const char *, const char *);
|
||||
int bch2_parse_mount_opts(struct bch_fs *, struct bch_opts *, struct printbuf *,
|
||||
char *);
|
||||
char *, bool);
|
||||
|
||||
/* inode opts: */
|
||||
|
||||
|
@ -276,6 +276,25 @@ void bch2_printbuf_indent_add(struct printbuf *buf, unsigned spaces)
|
||||
buf->has_indent_or_tabstops = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* bch2_printbuf_indent_add_nextline() - add to the current indent level for
|
||||
* subsequent lines
|
||||
*
|
||||
* @buf: printbuf to control
|
||||
* @spaces: number of spaces to add to the current indent level
|
||||
*
|
||||
* Subsequent lines - not the current line - will be indented by @spaces more
|
||||
* spaces.
|
||||
*/
|
||||
void bch2_printbuf_indent_add_nextline(struct printbuf *buf, unsigned spaces)
|
||||
{
|
||||
if (WARN_ON_ONCE(buf->indent + spaces < buf->indent))
|
||||
spaces = 0;
|
||||
|
||||
buf->indent += spaces;
|
||||
buf->has_indent_or_tabstops = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* bch2_printbuf_indent_sub() - subtract from the current indent level
|
||||
*
|
||||
|
@ -112,6 +112,7 @@ void bch2_printbuf_tabstop_pop(struct printbuf *);
|
||||
int bch2_printbuf_tabstop_push(struct printbuf *, unsigned);
|
||||
|
||||
void bch2_printbuf_indent_add(struct printbuf *, unsigned);
|
||||
void bch2_printbuf_indent_add_nextline(struct printbuf *, unsigned);
|
||||
void bch2_printbuf_indent_sub(struct printbuf *, unsigned);
|
||||
|
||||
void bch2_prt_newline(struct printbuf *);
|
||||
|
@ -16,10 +16,8 @@ void bch2_progress_init(struct progress_indicator_state *s,
|
||||
if (!(btree_id_mask & BIT_ULL(i)))
|
||||
continue;
|
||||
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_btree,
|
||||
.btree.id = i,
|
||||
};
|
||||
struct disk_accounting_pos acc;
|
||||
disk_accounting_key_init(acc, btree, .id = i);
|
||||
|
||||
u64 v;
|
||||
bch2_accounting_mem_read(c, disk_accounting_pos_to_bpos(&acc), &v, 1);
|
||||
|
@ -600,12 +600,13 @@ void bch2_rebalance_status_to_text(struct printbuf *out, struct bch_fs *c)
|
||||
struct bch_fs_rebalance *r = &c->rebalance;
|
||||
|
||||
/* print pending work */
|
||||
struct disk_accounting_pos acc = { .type = BCH_DISK_ACCOUNTING_rebalance_work, };
|
||||
struct disk_accounting_pos acc;
|
||||
disk_accounting_key_init(acc, rebalance_work);
|
||||
u64 v;
|
||||
bch2_accounting_mem_read(c, disk_accounting_pos_to_bpos(&acc), &v, 1);
|
||||
|
||||
prt_printf(out, "pending work:\t");
|
||||
prt_human_readable_u64(out, v);
|
||||
prt_human_readable_u64(out, v << 9);
|
||||
prt_printf(out, "\n\n");
|
||||
|
||||
prt_str(out, bch2_rebalance_state_strs[r->state]);
|
||||
|
@ -13,12 +13,12 @@
|
||||
#include "disk_accounting.h"
|
||||
#include "errcode.h"
|
||||
#include "error.h"
|
||||
#include "fs-common.h"
|
||||
#include "journal_io.h"
|
||||
#include "journal_reclaim.h"
|
||||
#include "journal_seq_blacklist.h"
|
||||
#include "logged_ops.h"
|
||||
#include "move.h"
|
||||
#include "namei.h"
|
||||
#include "quota.h"
|
||||
#include "rebalance.h"
|
||||
#include "recovery.h"
|
||||
|
@ -234,28 +234,22 @@ static int bch2_run_recovery_pass(struct bch_fs *c, enum bch_recovery_pass pass)
|
||||
|
||||
int bch2_run_online_recovery_passes(struct bch_fs *c)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
down_read(&c->state_lock);
|
||||
|
||||
for (unsigned i = 0; i < ARRAY_SIZE(recovery_pass_fns); i++) {
|
||||
struct recovery_pass_fn *p = recovery_pass_fns + i;
|
||||
|
||||
if (!(p->when & PASS_ONLINE))
|
||||
continue;
|
||||
|
||||
ret = bch2_run_recovery_pass(c, i);
|
||||
int ret = bch2_run_recovery_pass(c, i);
|
||||
if (bch2_err_matches(ret, BCH_ERR_restart_recovery)) {
|
||||
i = c->curr_recovery_pass;
|
||||
continue;
|
||||
}
|
||||
if (ret)
|
||||
break;
|
||||
return ret;
|
||||
}
|
||||
|
||||
up_read(&c->state_lock);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bch2_run_recovery_passes(struct bch_fs *c)
|
||||
|
@ -193,10 +193,10 @@ static int bch2_indirect_extent_missing_error(struct btree_trans *trans,
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
prt_printf(&buf, "-%llu\n ", (missing_pos.offset + (missing_end - missing_start)) << 9);
|
||||
prt_printf(&buf, "-%llu\n", (missing_pos.offset + (missing_end - missing_start)) << 9);
|
||||
bch2_bkey_val_to_text(&buf, c, p.s_c);
|
||||
|
||||
prt_printf(&buf, "\n missing reflink btree range %llu-%llu",
|
||||
prt_printf(&buf, "\nmissing reflink btree range %llu-%llu",
|
||||
missing_start, missing_end);
|
||||
|
||||
if (fsck_err(trans, reflink_p_to_missing_reflink_v, "%s", buf.buf)) {
|
||||
@ -323,10 +323,10 @@ static int trans_trigger_reflink_p_segment(struct btree_trans *trans,
|
||||
__le64 *refcount = bkey_refcount(bkey_i_to_s(new));
|
||||
if (!*refcount && (flags & BTREE_TRIGGER_overwrite)) {
|
||||
bch2_bkey_val_to_text(&buf, c, p.s_c);
|
||||
prt_printf(&buf, "\n ");
|
||||
prt_newline(&buf);
|
||||
bch2_bkey_val_to_text(&buf, c, k);
|
||||
log_fsck_err(trans, reflink_refcount_underflow,
|
||||
"indirect extent refcount underflow while marking\n %s",
|
||||
"indirect extent refcount underflow while marking\n%s",
|
||||
buf.buf);
|
||||
goto next;
|
||||
}
|
||||
@ -795,8 +795,8 @@ static int bch2_gc_write_reflink_key(struct btree_trans *trans,
|
||||
if (fsck_err_on(r->refcount != le64_to_cpu(*refcount),
|
||||
trans, reflink_v_refcount_wrong,
|
||||
"reflink key has wrong refcount:\n"
|
||||
" %s\n"
|
||||
" should be %u",
|
||||
"%s\n"
|
||||
"should be %u",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf),
|
||||
r->refcount)) {
|
||||
struct bkey_i *new = bch2_bkey_make_mut_noupdate(trans, k);
|
||||
|
@ -5,8 +5,7 @@
|
||||
enum bch_fsck_flags {
|
||||
FSCK_CAN_FIX = 1 << 0,
|
||||
FSCK_CAN_IGNORE = 1 << 1,
|
||||
FSCK_NO_RATELIMIT = 1 << 2,
|
||||
FSCK_AUTOFIX = 1 << 3,
|
||||
FSCK_AUTOFIX = 1 << 2,
|
||||
};
|
||||
|
||||
#define BCH_SB_ERRS() \
|
||||
@ -311,13 +310,14 @@ enum bch_fsck_flags {
|
||||
x(accounting_key_replicas_nr_required_bad, 279, FSCK_AUTOFIX) \
|
||||
x(accounting_key_replicas_devs_unsorted, 280, FSCK_AUTOFIX) \
|
||||
x(accounting_key_version_0, 282, FSCK_AUTOFIX) \
|
||||
x(accounting_key_nr_counters_wrong, 307, FSCK_AUTOFIX) \
|
||||
x(logged_op_but_clean, 283, FSCK_AUTOFIX) \
|
||||
x(compression_opt_not_marked_in_sb, 295, FSCK_AUTOFIX) \
|
||||
x(compression_type_not_marked_in_sb, 296, FSCK_AUTOFIX) \
|
||||
x(directory_size_mismatch, 303, FSCK_AUTOFIX) \
|
||||
x(dirent_cf_name_too_big, 304, 0) \
|
||||
x(dirent_stray_data_after_cf_name, 305, 0) \
|
||||
x(MAX, 307, 0)
|
||||
x(MAX, 308, 0)
|
||||
|
||||
enum bch_sb_error_id {
|
||||
#define x(t, n, ...) BCH_FSCK_ERR_##t = n,
|
||||
|
@ -485,7 +485,7 @@ static int check_snapshot_tree(struct btree_trans *trans,
|
||||
root_id != bch2_snapshot_root(c, root_id) ||
|
||||
st.k->p.offset != le32_to_cpu(s.tree),
|
||||
trans, snapshot_tree_to_missing_snapshot,
|
||||
"snapshot tree points to missing/incorrect snapshot:\n %s",
|
||||
"snapshot tree points to missing/incorrect snapshot:\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, st.s_c),
|
||||
prt_newline(&buf),
|
||||
ret
|
||||
@ -505,19 +505,19 @@ static int check_snapshot_tree(struct btree_trans *trans,
|
||||
|
||||
if (fsck_err_on(ret,
|
||||
trans, snapshot_tree_to_missing_subvol,
|
||||
"snapshot tree points to missing subvolume:\n %s",
|
||||
"snapshot tree points to missing subvolume:\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf)) ||
|
||||
fsck_err_on(!bch2_snapshot_is_ancestor(c,
|
||||
le32_to_cpu(subvol.snapshot),
|
||||
root_id),
|
||||
trans, snapshot_tree_to_wrong_subvol,
|
||||
"snapshot tree points to subvolume that does not point to snapshot in this tree:\n %s",
|
||||
"snapshot tree points to subvolume that does not point to snapshot in this tree:\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf)) ||
|
||||
fsck_err_on(BCH_SUBVOLUME_SNAP(&subvol),
|
||||
trans, snapshot_tree_to_snapshot_subvol,
|
||||
"snapshot tree points to snapshot subvolume:\n %s",
|
||||
"snapshot tree points to snapshot subvolume:\n%s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf))) {
|
||||
struct bkey_i_snapshot_tree *u;
|
||||
@ -756,7 +756,7 @@ static int check_snapshot(struct btree_trans *trans,
|
||||
} else {
|
||||
if (fsck_err_on(s.subvol,
|
||||
trans, snapshot_should_not_have_subvol,
|
||||
"snapshot should not point to subvol:\n %s",
|
||||
"snapshot should not point to subvol:\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot);
|
||||
ret = PTR_ERR_OR_ZERO(u);
|
||||
@ -774,7 +774,7 @@ static int check_snapshot(struct btree_trans *trans,
|
||||
|
||||
if (fsck_err_on(!ret,
|
||||
trans, snapshot_to_bad_snapshot_tree,
|
||||
"snapshot points to missing/incorrect tree:\n %s",
|
||||
"snapshot points to missing/incorrect tree:\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
ret = snapshot_tree_ptr_repair(trans, iter, k, &s);
|
||||
if (ret)
|
||||
@ -786,7 +786,7 @@ static int check_snapshot(struct btree_trans *trans,
|
||||
|
||||
if (fsck_err_on(le32_to_cpu(s.depth) != real_depth,
|
||||
trans, snapshot_bad_depth,
|
||||
"snapshot with incorrect depth field, should be %u:\n %s",
|
||||
"snapshot with incorrect depth field, should be %u:\n%s",
|
||||
real_depth, (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot);
|
||||
ret = PTR_ERR_OR_ZERO(u);
|
||||
@ -803,7 +803,7 @@ static int check_snapshot(struct btree_trans *trans,
|
||||
|
||||
if (fsck_err_on(!ret,
|
||||
trans, snapshot_bad_skiplist,
|
||||
"snapshot with bad skiplist field:\n %s",
|
||||
"snapshot with bad skiplist field:\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||
u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot);
|
||||
ret = PTR_ERR_OR_ZERO(u);
|
||||
|
@ -232,7 +232,7 @@ bad_hash:
|
||||
goto out;
|
||||
|
||||
if (fsck_err(trans, hash_table_key_wrong_offset,
|
||||
"hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n %s",
|
||||
"hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n%s",
|
||||
bch2_btree_id_str(desc->btree_id), hash_k.k->p.inode, hash_k.k->p.offset, hash,
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf))) {
|
||||
|
@ -561,6 +561,7 @@ int bch2_subvolume_unlink(struct btree_trans *trans, u32 subvolid)
|
||||
}
|
||||
|
||||
SET_BCH_SUBVOLUME_UNLINKED(&n->v, true);
|
||||
n->v.fs_path_parent = 0;
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
return ret;
|
||||
}
|
||||
|
@ -533,9 +533,11 @@ int bch2_fs_read_write(struct bch_fs *c)
|
||||
|
||||
int bch2_fs_read_write_early(struct bch_fs *c)
|
||||
{
|
||||
lockdep_assert_held(&c->state_lock);
|
||||
down_write(&c->state_lock);
|
||||
int ret = __bch2_fs_read_write(c, true);
|
||||
up_write(&c->state_lock);
|
||||
|
||||
return __bch2_fs_read_write(c, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Filesystem startup/shutdown: */
|
||||
@ -1019,38 +1021,39 @@ static void print_mount_opts(struct bch_fs *c)
|
||||
int bch2_fs_start(struct bch_fs *c)
|
||||
{
|
||||
time64_t now = ktime_get_real_seconds();
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
print_mount_opts(c);
|
||||
|
||||
down_write(&c->state_lock);
|
||||
mutex_lock(&c->sb_lock);
|
||||
|
||||
BUG_ON(test_bit(BCH_FS_started, &c->flags));
|
||||
|
||||
mutex_lock(&c->sb_lock);
|
||||
if (!bch2_sb_field_get_minsize(&c->disk_sb, ext,
|
||||
sizeof(struct bch_sb_field_ext) / sizeof(u64))) {
|
||||
mutex_unlock(&c->sb_lock);
|
||||
up_write(&c->state_lock);
|
||||
ret = -BCH_ERR_ENOSPC_sb;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = bch2_sb_members_v2_init(c);
|
||||
if (ret) {
|
||||
mutex_unlock(&c->sb_lock);
|
||||
up_write(&c->state_lock);
|
||||
goto err;
|
||||
}
|
||||
|
||||
for_each_online_member(c, ca)
|
||||
bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx)->last_mount = cpu_to_le64(now);
|
||||
|
||||
struct bch_sb_field_ext *ext =
|
||||
bch2_sb_field_get_minsize(&c->disk_sb, ext, sizeof(*ext) / sizeof(u64));
|
||||
mutex_unlock(&c->sb_lock);
|
||||
|
||||
if (!ext) {
|
||||
bch_err(c, "insufficient space in superblock for sb_field_ext");
|
||||
ret = -BCH_ERR_ENOSPC_sb;
|
||||
goto err;
|
||||
}
|
||||
|
||||
for_each_rw_member(c, ca)
|
||||
bch2_dev_allocator_add(c, ca);
|
||||
bch2_recalc_capacity(c);
|
||||
up_write(&c->state_lock);
|
||||
|
||||
c->recovery_task = current;
|
||||
ret = BCH_SB_INITIALIZED(c->disk_sb.sb)
|
||||
@ -1066,31 +1069,28 @@ int bch2_fs_start(struct bch_fs *c)
|
||||
goto err;
|
||||
|
||||
if (bch2_fs_init_fault("fs_start")) {
|
||||
bch_err(c, "fs_start fault injected");
|
||||
ret = -EINVAL;
|
||||
ret = -BCH_ERR_injected_fs_start;
|
||||
goto err;
|
||||
}
|
||||
|
||||
set_bit(BCH_FS_started, &c->flags);
|
||||
wake_up(&c->ro_ref_wait);
|
||||
|
||||
down_write(&c->state_lock);
|
||||
if (c->opts.read_only) {
|
||||
bch2_fs_read_only(c);
|
||||
} else {
|
||||
ret = !test_bit(BCH_FS_rw, &c->flags)
|
||||
? bch2_fs_read_write(c)
|
||||
: bch2_fs_read_write_late(c);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
up_write(&c->state_lock);
|
||||
|
||||
ret = 0;
|
||||
err:
|
||||
if (ret)
|
||||
bch_err_msg(c, ret, "starting filesystem");
|
||||
else
|
||||
bch_verbose(c, "done starting filesystem");
|
||||
up_write(&c->state_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1990,15 +1990,12 @@ int bch2_dev_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)
|
||||
mutex_unlock(&c->sb_lock);
|
||||
|
||||
if (ca->mi.freespace_initialized) {
|
||||
struct disk_accounting_pos acc = {
|
||||
.type = BCH_DISK_ACCOUNTING_dev_data_type,
|
||||
.dev_data_type.dev = ca->dev_idx,
|
||||
.dev_data_type.data_type = BCH_DATA_free,
|
||||
};
|
||||
u64 v[3] = { nbuckets - old_nbuckets, 0, 0 };
|
||||
|
||||
ret = bch2_trans_commit_do(ca->fs, NULL, NULL, 0,
|
||||
bch2_disk_accounting_mod(trans, &acc, v, ARRAY_SIZE(v), false)) ?:
|
||||
bch2_disk_accounting_mod2(trans, false, v, dev_data_type,
|
||||
.dev = ca->dev_idx,
|
||||
.data_type = BCH_DATA_free)) ?:
|
||||
bch2_dev_freespace_init(c, ca, old_nbuckets, nbuckets);
|
||||
if (ret)
|
||||
goto err;
|
||||
@ -2262,7 +2259,7 @@ BCH_DEBUG_PARAMS()
|
||||
|
||||
__maybe_unused
|
||||
static unsigned bch2_metadata_version = bcachefs_metadata_version_current;
|
||||
module_param_named(version, bch2_metadata_version, uint, 0400);
|
||||
module_param_named(version, bch2_metadata_version, uint, 0444);
|
||||
|
||||
module_exit(bcachefs_exit);
|
||||
module_init(bcachefs_init);
|
||||
|
@ -257,10 +257,8 @@ static int bch2_compression_stats_to_text(struct printbuf *out, struct bch_fs *c
|
||||
prt_printf(out, "type\tcompressed\runcompressed\raverage extent size\r\n");
|
||||
|
||||
for (unsigned i = 1; i < BCH_COMPRESSION_TYPE_NR; i++) {
|
||||
struct disk_accounting_pos a = {
|
||||
.type = BCH_DISK_ACCOUNTING_compression,
|
||||
.compression.type = i,
|
||||
};
|
||||
struct disk_accounting_pos a;
|
||||
disk_accounting_key_init(a, compression, .type = i);
|
||||
struct bpos p = disk_accounting_pos_to_bpos(&a);
|
||||
u64 v[3];
|
||||
bch2_accounting_mem_read(c, p, v, ARRAY_SIZE(v));
|
||||
@ -631,8 +629,6 @@ static ssize_t sysfs_opt_store(struct bch_fs *c,
|
||||
if (unlikely(!bch2_write_ref_tryget(c, BCH_WRITE_REF_sysfs)))
|
||||
return -EROFS;
|
||||
|
||||
down_write(&c->state_lock);
|
||||
|
||||
char *tmp = kstrdup(buf, GFP_KERNEL);
|
||||
if (!tmp) {
|
||||
ret = -ENOMEM;
|
||||
@ -675,7 +671,6 @@ static ssize_t sysfs_opt_store(struct bch_fs *c,
|
||||
|
||||
ret = size;
|
||||
err:
|
||||
up_write(&c->state_lock);
|
||||
bch2_write_ref_put(c, BCH_WRITE_REF_sysfs);
|
||||
return ret;
|
||||
}
|
||||
|
@ -10,6 +10,9 @@
|
||||
#include "eytzinger.h"
|
||||
#include "time_stats.h"
|
||||
|
||||
/* disable automatic switching to percpu mode */
|
||||
#define TIME_STATS_NONPCPU ((unsigned long) 1)
|
||||
|
||||
static const struct time_unit time_units[] = {
|
||||
{ "ns", 1 },
|
||||
{ "us", NSEC_PER_USEC },
|
||||
@ -123,11 +126,12 @@ void __bch2_time_stats_update(struct bch2_time_stats *stats, u64 start, u64 end)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!stats->buffer) {
|
||||
if ((unsigned long) stats->buffer <= TIME_STATS_NONPCPU) {
|
||||
spin_lock_irqsave(&stats->lock, flags);
|
||||
time_stats_update_one(stats, start, end);
|
||||
|
||||
if (mean_and_variance_weighted_get_mean(stats->freq_stats_weighted, TIME_STATS_MV_WEIGHT) < 32 &&
|
||||
if (!stats->buffer &&
|
||||
mean_and_variance_weighted_get_mean(stats->freq_stats_weighted, TIME_STATS_MV_WEIGHT) < 32 &&
|
||||
stats->duration_stats.n > 1024)
|
||||
stats->buffer =
|
||||
alloc_percpu_gfp(struct time_stat_buffer,
|
||||
@ -157,7 +161,7 @@ void bch2_time_stats_reset(struct bch2_time_stats *stats)
|
||||
unsigned offset = offsetof(struct bch2_time_stats, min_duration);
|
||||
memset((void *) stats + offset, 0, sizeof(*stats) - offset);
|
||||
|
||||
if (stats->buffer) {
|
||||
if ((unsigned long) stats->buffer > TIME_STATS_NONPCPU) {
|
||||
int cpu;
|
||||
for_each_possible_cpu(cpu)
|
||||
per_cpu_ptr(stats->buffer, cpu)->nr = 0;
|
||||
@ -167,7 +171,9 @@ void bch2_time_stats_reset(struct bch2_time_stats *stats)
|
||||
|
||||
void bch2_time_stats_exit(struct bch2_time_stats *stats)
|
||||
{
|
||||
free_percpu(stats->buffer);
|
||||
if ((unsigned long) stats->buffer > TIME_STATS_NONPCPU)
|
||||
free_percpu(stats->buffer);
|
||||
stats->buffer = NULL;
|
||||
}
|
||||
|
||||
void bch2_time_stats_init(struct bch2_time_stats *stats)
|
||||
@ -177,3 +183,9 @@ void bch2_time_stats_init(struct bch2_time_stats *stats)
|
||||
stats->min_freq = U64_MAX;
|
||||
spin_lock_init(&stats->lock);
|
||||
}
|
||||
|
||||
void bch2_time_stats_init_no_pcpu(struct bch2_time_stats *stats)
|
||||
{
|
||||
bch2_time_stats_init(stats);
|
||||
stats->buffer = (struct time_stat_buffer __percpu *) TIME_STATS_NONPCPU;
|
||||
}
|
||||
|
@ -145,6 +145,7 @@ static inline bool track_event_change(struct bch2_time_stats *stats, bool v)
|
||||
void bch2_time_stats_reset(struct bch2_time_stats *);
|
||||
void bch2_time_stats_exit(struct bch2_time_stats *);
|
||||
void bch2_time_stats_init(struct bch2_time_stats *);
|
||||
void bch2_time_stats_init_no_pcpu(struct bch2_time_stats *);
|
||||
|
||||
static inline void bch2_time_stats_quantiles_exit(struct bch2_time_stats_quantiles *statq)
|
||||
{
|
||||
|
@ -270,7 +270,7 @@ static void __bch2_print_string_as_lines(const char *prefix, const char *lines,
|
||||
locked = console_trylock();
|
||||
}
|
||||
|
||||
while (1) {
|
||||
while (*lines) {
|
||||
p = strchrnul(lines, '\n');
|
||||
printk("%s%.*s\n", prefix, (int) (p - lines), lines);
|
||||
if (!*p)
|
||||
|
@ -94,6 +94,7 @@ do { \
|
||||
#define printbuf_tabstop_push(_buf, _n) bch2_printbuf_tabstop_push(_buf, _n)
|
||||
|
||||
#define printbuf_indent_add(_out, _n) bch2_printbuf_indent_add(_out, _n)
|
||||
#define printbuf_indent_add_nextline(_out, _n) bch2_printbuf_indent_add_nextline(_out, _n)
|
||||
#define printbuf_indent_sub(_out, _n) bch2_printbuf_indent_sub(_out, _n)
|
||||
|
||||
#define prt_newline(_out) bch2_prt_newline(_out)
|
||||
@ -431,7 +432,7 @@ static inline void memcpy_u64s_small(void *dst, const void *src,
|
||||
static inline void __memcpy_u64s(void *dst, const void *src,
|
||||
unsigned u64s)
|
||||
{
|
||||
#ifdef CONFIG_X86_64
|
||||
#if defined(CONFIG_X86_64) && !defined(CONFIG_KMSAN)
|
||||
long d0, d1, d2;
|
||||
|
||||
asm volatile("rep ; movsq"
|
||||
@ -508,7 +509,7 @@ static inline void __memmove_u64s_up(void *_dst, const void *_src,
|
||||
u64 *dst = (u64 *) _dst + u64s - 1;
|
||||
u64 *src = (u64 *) _src + u64s - 1;
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
#if defined(CONFIG_X86_64) && !defined(CONFIG_KMSAN)
|
||||
long d0, d1, d2;
|
||||
|
||||
asm volatile("std ;\n"
|
||||
|
@ -25,28 +25,3 @@ enum Subcommands {
|
||||
#[command(visible_aliases = ["subvol"])]
|
||||
Subvolume(subvolume::Cli),
|
||||
}
|
||||
|
||||
// FIXME: Can be removed after bumping MSRV >= 1.77 in favor of `c""` literals
|
||||
#[macro_export]
|
||||
macro_rules! c_str {
|
||||
($lit:expr) => {
|
||||
::std::ffi::CStr::from_bytes_with_nul(concat!($lit, "\0").as_bytes())
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[test]
|
||||
fn check_cstr_macro() {
|
||||
let literal = c_str!("hello");
|
||||
|
||||
assert_eq!(
|
||||
literal,
|
||||
CStr::from_bytes_with_nul(b"hello\0").unwrap().as_ptr()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use std::{
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
use bch_bindgen::{bcachefs, bcachefs::bch_sb_handle, opt_set, path_to_cstr};
|
||||
use bcachefs::bch_opts;
|
||||
use clap::Parser;
|
||||
use log::{debug, error, info};
|
||||
use uuid::Uuid;
|
||||
@ -78,7 +79,7 @@ fn mount_inner(
|
||||
|
||||
/// Parse a comma-separated mount options and split out mountflags and filesystem
|
||||
/// specific options.
|
||||
fn parse_mount_options(options: impl AsRef<str>) -> (Option<String>, libc::c_ulong) {
|
||||
fn parse_mountflag_options(options: impl AsRef<str>) -> (Option<String>, libc::c_ulong) {
|
||||
use either::Either::{Left, Right};
|
||||
|
||||
debug!("parsing mount options: {}", options.as_ref());
|
||||
@ -120,8 +121,7 @@ fn parse_mount_options(options: impl AsRef<str>) -> (Option<String>, libc::c_ulo
|
||||
)
|
||||
}
|
||||
|
||||
fn read_super_silent(path: impl AsRef<Path>) -> anyhow::Result<bch_sb_handle> {
|
||||
let mut opts = bcachefs::bch_opts::default();
|
||||
fn read_super_silent(path: impl AsRef<Path>, mut opts: bch_opts) -> anyhow::Result<bch_sb_handle> {
|
||||
opt_set!(opts, noexcl, 1);
|
||||
|
||||
bch_bindgen::sb_io::read_super_silent(path.as_ref(), opts)
|
||||
@ -170,11 +170,11 @@ fn udev_bcachefs_info() -> anyhow::Result<HashMap<String, Vec<String>>> {
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
fn get_super_blocks(uuid: Uuid, devices: &[String]) -> Vec<(PathBuf, bch_sb_handle)> {
|
||||
fn get_super_blocks(uuid: Uuid, devices: &[String], opts: &bch_opts) -> Vec<(PathBuf, bch_sb_handle)> {
|
||||
devices
|
||||
.iter()
|
||||
.filter_map(|dev| {
|
||||
read_super_silent(PathBuf::from(dev))
|
||||
read_super_silent(PathBuf::from(dev), *opts)
|
||||
.ok()
|
||||
.map(|sb| (PathBuf::from(dev), sb))
|
||||
})
|
||||
@ -202,6 +202,7 @@ fn get_all_block_devnodes() -> anyhow::Result<Vec<String>> {
|
||||
fn get_devices_by_uuid(
|
||||
udev_bcachefs: &HashMap<String, Vec<String>>,
|
||||
uuid: Uuid,
|
||||
opts: &bch_opts
|
||||
) -> anyhow::Result<Vec<(PathBuf, bch_sb_handle)>> {
|
||||
let devices = {
|
||||
if !udev_bcachefs.is_empty() {
|
||||
@ -216,16 +217,17 @@ fn get_devices_by_uuid(
|
||||
}
|
||||
};
|
||||
|
||||
Ok(get_super_blocks(uuid, &devices))
|
||||
Ok(get_super_blocks(uuid, &devices, opts))
|
||||
}
|
||||
|
||||
fn devs_str_sbs_from_uuid(
|
||||
udev_info: &HashMap<String, Vec<String>>,
|
||||
uuid: &str,
|
||||
opts: &bch_opts
|
||||
) -> anyhow::Result<(String, Vec<bch_sb_handle>)> {
|
||||
debug!("enumerating devices with UUID {}", uuid);
|
||||
|
||||
let devs_sbs = Uuid::parse_str(uuid).map(|uuid| get_devices_by_uuid(udev_info, uuid))??;
|
||||
let devs_sbs = Uuid::parse_str(uuid).map(|uuid| get_devices_by_uuid(udev_info, uuid, opts))??;
|
||||
|
||||
let devs_str = devs_sbs
|
||||
.iter()
|
||||
@ -241,15 +243,16 @@ fn devs_str_sbs_from_uuid(
|
||||
fn devs_str_sbs_from_device(
|
||||
udev_info: &HashMap<String, Vec<String>>,
|
||||
device: &Path,
|
||||
opts: &bch_opts
|
||||
) -> anyhow::Result<(String, Vec<bch_sb_handle>)> {
|
||||
let dev_sb = read_super_silent(device)?;
|
||||
let dev_sb = read_super_silent(device, *opts)?;
|
||||
|
||||
if dev_sb.sb().number_of_devices() == 1 {
|
||||
Ok((device.as_os_str().to_str().unwrap().to_string(), vec![dev_sb]))
|
||||
} else {
|
||||
let uuid = dev_sb.sb().uuid();
|
||||
|
||||
devs_str_sbs_from_uuid(udev_info, &uuid.to_string())
|
||||
devs_str_sbs_from_uuid(udev_info, &uuid.to_string(), opts)
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,12 +274,16 @@ fn handle_unlock(cli: &Cli, sb: &bch_sb_handle) -> Result<KeyHandle> {
|
||||
}
|
||||
|
||||
fn cmd_mount_inner(cli: &Cli) -> Result<()> {
|
||||
let (optstr, mountflags) = parse_mountflag_options(&cli.options);
|
||||
let opts = bch_bindgen::opts::parse_mount_opts(None, optstr.as_deref(), true)
|
||||
.unwrap_or_default();
|
||||
|
||||
// Grab the udev information once
|
||||
let udev_info = udev_bcachefs_info()?;
|
||||
|
||||
let (devices, mut sbs) =
|
||||
if let Some(("UUID" | "OLD_BLKID_UUID", uuid)) = cli.dev.split_once('=') {
|
||||
devs_str_sbs_from_uuid(&udev_info, uuid)?
|
||||
devs_str_sbs_from_uuid(&udev_info, uuid, &opts)?
|
||||
} else if cli.dev.contains(':') {
|
||||
// If the device string contains ":" we will assume the user knows the
|
||||
// entire list. If they supply a single device it could be either the FS
|
||||
@ -287,12 +294,12 @@ fn cmd_mount_inner(cli: &Cli) -> Result<()> {
|
||||
let sbs = cli
|
||||
.dev
|
||||
.split(':')
|
||||
.map(read_super_silent)
|
||||
.map(|path| read_super_silent(path, opts))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
(cli.dev.clone(), sbs)
|
||||
} else {
|
||||
devs_str_sbs_from_device(&udev_info, Path::new(&cli.dev))?
|
||||
devs_str_sbs_from_device(&udev_info, Path::new(&cli.dev), &opts)?
|
||||
};
|
||||
|
||||
ensure!(!sbs.is_empty(), "No device(s) to mount specified");
|
||||
@ -317,8 +324,7 @@ fn cmd_mount_inner(cli: &Cli) -> Result<()> {
|
||||
&cli.options
|
||||
);
|
||||
|
||||
let (data, mountflags) = parse_mount_options(&cli.options);
|
||||
mount_inner(devices, mountpoint, "bcachefs", mountflags, data)
|
||||
mount_inner(devices, mountpoint, "bcachefs", mountflags, optstr)
|
||||
} else {
|
||||
info!(
|
||||
"would mount with params: device: {}, options: {}",
|
||||
|
@ -18,12 +18,14 @@ enum Subcommands {
|
||||
#[command(visible_aliases = ["new"])]
|
||||
Create {
|
||||
/// Paths
|
||||
#[arg(required = true)]
|
||||
targets: Vec<PathBuf>,
|
||||
},
|
||||
|
||||
#[command(visible_aliases = ["del"])]
|
||||
Delete {
|
||||
/// Path
|
||||
#[arg(required = true)]
|
||||
targets: Vec<PathBuf>,
|
||||
},
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user