mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-02-22 00:00:03 +03:00
update bcache code, fsck improvements
This commit is contained in:
parent
ab88049a08
commit
5933f9478c
@ -1 +1 @@
|
|||||||
BCACHE_REVISION=f8c8c133492ac9a63fdfeb9edf9bb26a3283db9f
|
BCACHE_REVISION=76e3b2312705df2cb5adb8834bc6df56a288932e
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,3 +7,4 @@ probe-bcache
|
|||||||
*.a
|
*.a
|
||||||
tags
|
tags
|
||||||
cscope*
|
cscope*
|
||||||
|
bcache-tools
|
||||||
|
26
Makefile
26
Makefile
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
PREFIX=/usr
|
PREFIX=/usr
|
||||||
INSTALL=install
|
INSTALL=install
|
||||||
CFLAGS+=-std=gnu99 -O2 -g -flto -MMD -Wall \
|
CFLAGS+=-std=gnu99 -O2 -g -MMD -Wall \
|
||||||
-Wno-unused-but-set-variable \
|
-Wno-unused-but-set-variable \
|
||||||
-Wno-pointer-sign \
|
-Wno-pointer-sign \
|
||||||
-fno-strict-aliasing \
|
-fno-strict-aliasing \
|
||||||
@ -11,7 +11,14 @@ CFLAGS+=-std=gnu99 -O2 -g -flto -MMD -Wall \
|
|||||||
-D_LGPL_SOURCE \
|
-D_LGPL_SOURCE \
|
||||||
-DRCU_MEMBARRIER \
|
-DRCU_MEMBARRIER \
|
||||||
$(EXTRA_CFLAGS)
|
$(EXTRA_CFLAGS)
|
||||||
LDFLAGS+=-O2 -g -flto
|
LDFLAGS+=-O2 -g
|
||||||
|
|
||||||
|
ifdef D
|
||||||
|
CFLAGS+=-Werror
|
||||||
|
else
|
||||||
|
CFLAGS+=-flto -Werror
|
||||||
|
LDFLAGS+=-flto
|
||||||
|
endif
|
||||||
|
|
||||||
PKGCONFIG_LIBS="blkid uuid liburcu"
|
PKGCONFIG_LIBS="blkid uuid liburcu"
|
||||||
CFLAGS+=`pkg-config --cflags ${PKGCONFIG_LIBS}`
|
CFLAGS+=`pkg-config --cflags ${PKGCONFIG_LIBS}`
|
||||||
@ -33,9 +40,18 @@ CCANOBJS=$(patsubst %.c,%.o,$(CCANSRCS))
|
|||||||
LINUX_SRCS=$(wildcard linux/*.c linux/*/*.c)
|
LINUX_SRCS=$(wildcard linux/*.c linux/*/*.c)
|
||||||
LINUX_OBJS=$(LINUX_SRCS:.c=.o)
|
LINUX_OBJS=$(LINUX_SRCS:.c=.o)
|
||||||
|
|
||||||
OBJS=bcache.o bcache-assemble.o bcache-device.o bcache-format.o \
|
OBJS=bcache.o \
|
||||||
bcache-fs.o bcache-run.o bcache-userspace-shim.o \
|
bcache-userspace-shim.o \
|
||||||
libbcache.o tools-util.o $(LINUX_OBJS) $(CCANOBJS)
|
cmd_assemble.o \
|
||||||
|
cmd_device.o \
|
||||||
|
cmd_fs.o \
|
||||||
|
cmd_fsck.o \
|
||||||
|
cmd_format.o \
|
||||||
|
cmd_run.o \
|
||||||
|
libbcache.o \
|
||||||
|
tools-util.o \
|
||||||
|
$(LINUX_OBJS) \
|
||||||
|
$(CCANOBJS)
|
||||||
|
|
||||||
DEPS=$(OBJS:.o=.d)
|
DEPS=$(OBJS:.o=.d)
|
||||||
-include $(DEPS)
|
-include $(DEPS)
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <linux/byteorder.h>
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include "libbcache.h"
|
||||||
|
#include "tools-util.h"
|
||||||
|
|
||||||
/* stub out the bcache code we aren't building: */
|
/* stub out the bcache code we aren't building: */
|
||||||
|
|
||||||
struct block_device;
|
struct block_device;
|
||||||
@ -53,6 +55,56 @@ void bch_cache_accounting_destroy(struct cache_accounting *acc) {}
|
|||||||
void bch_cache_accounting_init(struct cache_accounting *acc,
|
void bch_cache_accounting_init(struct cache_accounting *acc,
|
||||||
struct closure *parent) {}
|
struct closure *parent) {}
|
||||||
|
|
||||||
|
#define bch_fmt(_c, fmt) fmt "\n"
|
||||||
|
|
||||||
|
enum fsck_err_opts fsck_err_opt;
|
||||||
|
|
||||||
|
/* Returns true if error should be fixed: */
|
||||||
|
|
||||||
|
/* XXX: flag if we ignore errors */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's an error that we can't ignore, and we're running non
|
||||||
|
* interactively - return true and have the error fixed so that we don't have to
|
||||||
|
* bail out and stop the fsck early, so that the user can see all the errors
|
||||||
|
* present:
|
||||||
|
*/
|
||||||
|
#define __fsck_err(c, _can_fix, _can_ignore, _nofix_msg, msg, ...) \
|
||||||
|
({ \
|
||||||
|
bool _fix = false; \
|
||||||
|
\
|
||||||
|
if (_can_fix) { \
|
||||||
|
switch (fsck_err_opt) { \
|
||||||
|
case FSCK_ERR_ASK: \
|
||||||
|
printf(msg ": fix?", ##__VA_ARGS__); \
|
||||||
|
_fix = ask_yn(); \
|
||||||
|
\
|
||||||
|
break; \
|
||||||
|
case FSCK_ERR_YES: \
|
||||||
|
bch_err(c, msg ", fixing", ##__VA_ARGS__); \
|
||||||
|
_fix = true; \
|
||||||
|
break; \
|
||||||
|
case FSCK_ERR_NO: \
|
||||||
|
bch_err(c, msg, ##__VA_ARGS__); \
|
||||||
|
_fix = false; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} else if (_can_ignore) { \
|
||||||
|
bch_err(c, msg, ##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
if (_can_fix && !_can_ignore && fsck_err_opt == FSCK_ERR_NO) \
|
||||||
|
_fix = true; \
|
||||||
|
\
|
||||||
|
if (!_fix && !_can_ignore) { \
|
||||||
|
printf("Fatal filesystem inconsistency, halting\n"); \
|
||||||
|
ret = BCH_FSCK_ERRORS_NOT_FIXED; \
|
||||||
|
goto fsck_err; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
_fix; \
|
||||||
|
})
|
||||||
|
|
||||||
//#include "acl.c"
|
//#include "acl.c"
|
||||||
#include "alloc.c"
|
#include "alloc.c"
|
||||||
#include "bkey.c"
|
#include "bkey.c"
|
||||||
@ -113,31 +165,3 @@ SHIM_KTYPE(bch_cache_set);
|
|||||||
SHIM_KTYPE(bch_cache_set_internal);
|
SHIM_KTYPE(bch_cache_set_internal);
|
||||||
SHIM_KTYPE(bch_cache_set_time_stats);
|
SHIM_KTYPE(bch_cache_set_time_stats);
|
||||||
SHIM_KTYPE(bch_cache_set_opts_dir);
|
SHIM_KTYPE(bch_cache_set_opts_dir);
|
||||||
|
|
||||||
//#include "tools-util.h"
|
|
||||||
|
|
||||||
int cmd_fsck(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
DECLARE_COMPLETION_ONSTACK(shutdown);
|
|
||||||
struct cache_set_opts opts = cache_set_opts_empty();
|
|
||||||
struct cache_set *c = NULL;
|
|
||||||
const char *err;
|
|
||||||
|
|
||||||
printf("registering %s...\n", argv[1]);
|
|
||||||
|
|
||||||
err = bch_register_cache_set(argv + 1, argc - 1, opts, &c);
|
|
||||||
if (err) {
|
|
||||||
BUG_ON(c);
|
|
||||||
fprintf(stderr, "error opening %s: %s\n", argv[1], err);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
c->stop_completion = &shutdown;
|
|
||||||
bch_cache_set_stop(c);
|
|
||||||
closure_put(&c->cl);
|
|
||||||
|
|
||||||
/* Killable? */
|
|
||||||
wait_for_completion(&shutdown);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
2
bcache.c
2
bcache.c
@ -21,7 +21,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "bcache-cmds.h"
|
#include "cmds.h"
|
||||||
|
|
||||||
static void usage(void)
|
static void usage(void)
|
||||||
{
|
{
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include "bcache-cmds.h"
|
#include "cmds.h"
|
||||||
|
#include "linux/bcache-ioctl.h"
|
||||||
|
|
||||||
int cmd_assemble(int argc, char *argv[])
|
int cmd_assemble(int argc, char *argv[])
|
||||||
{
|
{
|
@ -12,8 +12,9 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "bcache-cmds.h"
|
#include "cmds.h"
|
||||||
#include "libbcache.h"
|
#include "libbcache.h"
|
||||||
|
#include "linux/bcache-ioctl.h"
|
||||||
|
|
||||||
/* This code belongs under show_fs */
|
/* This code belongs under show_fs */
|
||||||
#if 0
|
#if 0
|
||||||
@ -93,10 +94,10 @@ int cmd_device_show(int argc, char *argv[])
|
|||||||
};
|
};
|
||||||
char **args = bch_nih_init(argc, argv, opts);
|
char **args = bch_nih_init(argc, argv, opts);
|
||||||
|
|
||||||
if (nr_args(args) != 1)
|
if (argc != 2)
|
||||||
die("Please supply a single device");
|
die("Please supply a single device");
|
||||||
|
|
||||||
struct bcache_handle fs = bcache_fs_open(args[0]);
|
struct bcache_handle fs = bcache_fs_open(argv[1]);
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
|
|
||||||
struct bcache_dev devices[256];
|
struct bcache_dev devices[256];
|
@ -22,8 +22,10 @@
|
|||||||
|
|
||||||
#include "ccan/darray/darray.h"
|
#include "ccan/darray/darray.h"
|
||||||
|
|
||||||
#include "bcache-cmds.h"
|
#include "cmds.h"
|
||||||
#include "libbcache.h"
|
#include "libbcache.h"
|
||||||
|
#include "opts.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
/* Open a block device, do magic blkid stuff: */
|
/* Open a block device, do magic blkid stuff: */
|
||||||
static int open_for_format(const char *dev, bool force)
|
static int open_for_format(const char *dev, bool force)
|
||||||
@ -58,7 +60,8 @@ static int open_for_format(const char *dev, bool force)
|
|||||||
else
|
else
|
||||||
printf("%s contains a %s filesystem\n",
|
printf("%s contains a %s filesystem\n",
|
||||||
dev, fs_type);
|
dev, fs_type);
|
||||||
if (!ask_proceed())
|
fputs("Proceed anyway?", stdout);
|
||||||
|
if (!ask_yn())
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +99,6 @@ static void usage(void)
|
|||||||
" bcache format --tier 0 /dev/sdb --tier 1 /dev/sdc\n"
|
" bcache format --tier 0 /dev/sdb --tier 1 /dev/sdc\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Report bugs to <linux-bcache@vger.kernel.org>");
|
"Report bugs to <linux-bcache@vger.kernel.org>");
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define OPTS \
|
#define OPTS \
|
||||||
@ -132,6 +134,27 @@ static const struct option format_opts[] = {
|
|||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static unsigned hatoi_validate(const char *s, const char *msg)
|
||||||
|
{
|
||||||
|
u64 v;
|
||||||
|
|
||||||
|
if (bch_strtoull_h(s, &v))
|
||||||
|
die("bad %s %s", msg, s);
|
||||||
|
|
||||||
|
if (v & (v - 1))
|
||||||
|
die("%s must be a power of two", msg);
|
||||||
|
|
||||||
|
v /= 512;
|
||||||
|
|
||||||
|
if (v > USHRT_MAX)
|
||||||
|
die("%s too large\n", msg);
|
||||||
|
|
||||||
|
if (!v)
|
||||||
|
die("%s too small\n", msg);
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
int cmd_format(int argc, char *argv[])
|
int cmd_format(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
darray(struct dev_opts) devices;
|
darray(struct dev_opts) devices;
|
||||||
@ -174,20 +197,21 @@ int cmd_format(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
case Opt_metadata_checksum_type:
|
case Opt_metadata_checksum_type:
|
||||||
meta_csum_type = read_string_list_or_die(optarg,
|
meta_csum_type = read_string_list_or_die(optarg,
|
||||||
csum_types, "checksum type");
|
bch_csum_types, "checksum type");
|
||||||
break;
|
break;
|
||||||
case Opt_data_checksum_type:
|
case Opt_data_checksum_type:
|
||||||
data_csum_type = read_string_list_or_die(optarg,
|
data_csum_type = read_string_list_or_die(optarg,
|
||||||
csum_types, "checksum type");
|
bch_csum_types, "checksum type");
|
||||||
break;
|
break;
|
||||||
case Opt_compression_type:
|
case Opt_compression_type:
|
||||||
compression_type = read_string_list_or_die(optarg,
|
compression_type = read_string_list_or_die(optarg,
|
||||||
compression_types, "compression type");
|
bch_compression_types,
|
||||||
|
"compression type");
|
||||||
break;
|
break;
|
||||||
case Opt_error_action:
|
case Opt_error_action:
|
||||||
case 'e':
|
case 'e':
|
||||||
on_error_action = read_string_list_or_die(optarg,
|
on_error_action = read_string_list_or_die(optarg,
|
||||||
error_actions, "error action");
|
bch_error_actions, "error action");
|
||||||
break;
|
break;
|
||||||
case Opt_max_journal_entry_size:
|
case Opt_max_journal_entry_size:
|
||||||
max_journal_entry_size = hatoi_validate(optarg,
|
max_journal_entry_size = hatoi_validate(optarg,
|
||||||
@ -207,14 +231,19 @@ int cmd_format(int argc, char *argv[])
|
|||||||
force = true;
|
force = true;
|
||||||
break;
|
break;
|
||||||
case Opt_fs_size:
|
case Opt_fs_size:
|
||||||
filesystem_size = hatoi(optarg) >> 9;
|
if (bch_strtoull_h(optarg, &filesystem_size))
|
||||||
|
die("invalid filesystem size");
|
||||||
|
|
||||||
|
filesystem_size >>= 9;
|
||||||
break;
|
break;
|
||||||
case Opt_bucket_size:
|
case Opt_bucket_size:
|
||||||
bucket_size = hatoi_validate(optarg, "bucket size");
|
bucket_size = hatoi_validate(optarg, "bucket size");
|
||||||
break;
|
break;
|
||||||
case Opt_tier:
|
case Opt_tier:
|
||||||
case 't':
|
case 't':
|
||||||
tier = strtoul_or_die(optarg, CACHE_TIERS, "tier");
|
if (kstrtouint(optarg, 10, &tier) ||
|
||||||
|
tier >= CACHE_TIERS)
|
||||||
|
die("invalid tier");
|
||||||
break;
|
break;
|
||||||
case Opt_discard:
|
case Opt_discard:
|
||||||
discard = true;
|
discard = true;
|
||||||
@ -231,6 +260,7 @@ int cmd_format(int argc, char *argv[])
|
|||||||
case Opt_help:
|
case Opt_help:
|
||||||
case 'h':
|
case 'h':
|
||||||
usage();
|
usage();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
#include "bcache-cmds.h"
|
#include "cmds.h"
|
||||||
|
|
||||||
struct bcache_fs {
|
struct bcache_fs {
|
||||||
/* options... */
|
/* options... */
|
68
cmd_fsck.c
Normal file
68
cmd_fsck.c
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
|
||||||
|
#include "cmds.h"
|
||||||
|
#include "libbcache.h"
|
||||||
|
#include "super.h"
|
||||||
|
#include "tools-util.h"
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
puts("bcache fsck - filesystem check and repair\n"
|
||||||
|
"Usage: bcache fsck [OPTION]... <devices>\n"
|
||||||
|
"\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -p Automatic repair (no questions\n"
|
||||||
|
" -n Don't repair, only check for errors\n"
|
||||||
|
" -y Assume \"yes\" to all questions\n"
|
||||||
|
" -f Force checking even if filesystem is marked clean\n"
|
||||||
|
" -v Be verbose\n"
|
||||||
|
" --h Display this help and exit\n"
|
||||||
|
"Report bugs to <linux-bcache@vger.kernel.org>");
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_fsck(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
DECLARE_COMPLETION_ONSTACK(shutdown);
|
||||||
|
struct cache_set_opts opts = cache_set_opts_empty();
|
||||||
|
struct cache_set *c = NULL;
|
||||||
|
const char *err;
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "pynfvh")) != -1)
|
||||||
|
switch (opt) {
|
||||||
|
case 'p':
|
||||||
|
fsck_err_opt = FSCK_ERR_YES;
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
fsck_err_opt = FSCK_ERR_YES;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
opts.nochanges = true;
|
||||||
|
fsck_err_opt = FSCK_ERR_NO;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
/* force check, even if filesystem marked clean: */
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
opts.verbose_recovery = true;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
usage();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind >= argc)
|
||||||
|
die("Please supply device(s) to check");
|
||||||
|
|
||||||
|
err = bch_register_cache_set(argv + optind, argc - optind, opts, &c);
|
||||||
|
if (err)
|
||||||
|
die("error opening %s: %s", argv[optind], err);
|
||||||
|
|
||||||
|
c->stop_completion = &shutdown;
|
||||||
|
bch_cache_set_stop(c);
|
||||||
|
closure_put(&c->cl);
|
||||||
|
|
||||||
|
/* Killable? */
|
||||||
|
wait_for_completion(&shutdown);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -11,7 +11,8 @@
|
|||||||
|
|
||||||
#include <uuid/uuid.h>
|
#include <uuid/uuid.h>
|
||||||
|
|
||||||
#include "bcache-cmds.h"
|
#include "cmds.h"
|
||||||
|
#include "linux/bcache-ioctl.h"
|
||||||
|
|
||||||
int cmd_run(int argc, char *argv[])
|
int cmd_run(int argc, char *argv[])
|
||||||
{
|
{
|
@ -4,8 +4,8 @@
|
|||||||
* GPLv2
|
* GPLv2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _BCACHE_H
|
#ifndef _CMDS_H
|
||||||
#define _BCACHE_H
|
#define _CMDS_H
|
||||||
|
|
||||||
#include "tools-util.h"
|
#include "tools-util.h"
|
||||||
|
|
||||||
@ -25,4 +25,4 @@ int cmd_device_remove(int argc, char *argv[]);
|
|||||||
|
|
||||||
int cmd_fsck(int argc, char *argv[]);
|
int cmd_fsck(int argc, char *argv[]);
|
||||||
|
|
||||||
#endif /* _BCACHE_H */
|
#endif /* _CMDS_H */
|
@ -8,6 +8,7 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
|
#include <linux/byteorder.h>
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
|
|
||||||
#define IS_ENABLED(opt) 0
|
#define IS_ENABLED(opt) 0
|
||||||
|
@ -141,4 +141,14 @@ static inline u64 ktime_get_seconds(void)
|
|||||||
return ts.tv_sec;
|
return ts.tv_sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct timespec current_kernel_time(void)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CURRENT_TIME (current_kernel_time())
|
||||||
|
|
||||||
#endif /* __TOOLS_LINUX_SCHED_H */
|
#endif /* __TOOLS_LINUX_SCHED_H */
|
||||||
|
@ -61,6 +61,11 @@ static inline struct timespec64 timespec_to_timespec64(const struct timespec ts)
|
|||||||
# define ns_to_timespec64 ns_to_timespec
|
# define ns_to_timespec64 ns_to_timespec
|
||||||
# define timespec64_add_ns timespec_add_ns
|
# define timespec64_add_ns timespec_add_ns
|
||||||
|
|
||||||
|
static inline s64 timespec_to_ns(const struct timespec *ts)
|
||||||
|
{
|
||||||
|
return ((s64) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline struct timespec timespec64_to_timespec(const struct timespec64 ts64)
|
static inline struct timespec timespec64_to_timespec(const struct timespec64 ts64)
|
||||||
|
112
libbcache.c
112
libbcache.c
@ -14,74 +14,7 @@
|
|||||||
#include "linux/bcache.h"
|
#include "linux/bcache.h"
|
||||||
#include "libbcache.h"
|
#include "libbcache.h"
|
||||||
#include "checksum.h"
|
#include "checksum.h"
|
||||||
|
#include "opts.h"
|
||||||
const char * const cache_state[] = {
|
|
||||||
"active",
|
|
||||||
"ro",
|
|
||||||
"failed",
|
|
||||||
"spare",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * const replacement_policies[] = {
|
|
||||||
"lru",
|
|
||||||
"fifo",
|
|
||||||
"random",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * const csum_types[] = {
|
|
||||||
"none",
|
|
||||||
"crc32c",
|
|
||||||
"crc64",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * const compression_types[] = {
|
|
||||||
"none",
|
|
||||||
"lz4",
|
|
||||||
"gzip",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * const str_hash_types[] = {
|
|
||||||
"crc32c",
|
|
||||||
"crc64",
|
|
||||||
"siphash",
|
|
||||||
"sha1",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * const error_actions[] = {
|
|
||||||
"continue",
|
|
||||||
"readonly",
|
|
||||||
"panic",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * const member_states[] = {
|
|
||||||
"active",
|
|
||||||
"ro",
|
|
||||||
"failed",
|
|
||||||
"spare",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * const bdev_cache_mode[] = {
|
|
||||||
"writethrough",
|
|
||||||
"writeback",
|
|
||||||
"writearound",
|
|
||||||
"none",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * const bdev_state[] = {
|
|
||||||
"detached",
|
|
||||||
"clean",
|
|
||||||
"dirty",
|
|
||||||
"inconsistent",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
#define BCH_MIN_NR_NBUCKETS (1 << 10)
|
#define BCH_MIN_NR_NBUCKETS (1 << 10)
|
||||||
|
|
||||||
@ -93,15 +26,10 @@ void __do_write_sb(int fd, void *sb, size_t bytes)
|
|||||||
char zeroes[SB_SECTOR << 9] = {0};
|
char zeroes[SB_SECTOR << 9] = {0};
|
||||||
|
|
||||||
/* Zero start of disk */
|
/* Zero start of disk */
|
||||||
if (pwrite(fd, zeroes, SB_SECTOR << 9, 0) != SB_SECTOR << 9) {
|
xpwrite(fd, zeroes, SB_SECTOR << 9, 0);
|
||||||
perror("write error trying to zero start of disk\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
/* Write superblock */
|
/* Write superblock */
|
||||||
if (pwrite(fd, sb, bytes, SB_SECTOR << 9) != bytes) {
|
xpwrite(fd, sb, bytes, SB_SECTOR << 9);
|
||||||
perror("write error trying to write superblock\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
fsync(fd);
|
fsync(fd);
|
||||||
close(fd);
|
close(fd);
|
||||||
@ -142,7 +70,7 @@ void bcache_format(struct dev_opts *devs, size_t nr_devs,
|
|||||||
/* calculate bucket sizes: */
|
/* calculate bucket sizes: */
|
||||||
for (i = devs; i < devs + nr_devs; i++) {
|
for (i = devs; i < devs + nr_devs; i++) {
|
||||||
if (!i->size)
|
if (!i->size)
|
||||||
i->size = get_size(i->path, i->fd);
|
i->size = get_size(i->path, i->fd) >> 9;
|
||||||
|
|
||||||
if (!i->bucket_size) {
|
if (!i->bucket_size) {
|
||||||
if (i->size < min_size(block_size))
|
if (i->size < min_size(block_size))
|
||||||
@ -297,12 +225,12 @@ void bcache_super_print(struct cache_sb *sb, int units)
|
|||||||
internal_uuid_str,
|
internal_uuid_str,
|
||||||
label,
|
label,
|
||||||
le64_to_cpu(sb->version),
|
le64_to_cpu(sb->version),
|
||||||
pr_units(le16_to_cpu(sb->block_size), units).b,
|
pr_units(le16_to_cpu(sb->block_size), units),
|
||||||
pr_units(CACHE_SET_BTREE_NODE_SIZE(sb), units).b,
|
pr_units(CACHE_SET_BTREE_NODE_SIZE(sb), units),
|
||||||
pr_units(1U << CACHE_SET_JOURNAL_ENTRY_SIZE(sb), units).b,
|
pr_units(1U << CACHE_SET_JOURNAL_ENTRY_SIZE(sb), units),
|
||||||
|
|
||||||
CACHE_SET_ERROR_ACTION(sb) < BCH_NR_ERROR_ACTIONS
|
CACHE_SET_ERROR_ACTION(sb) < BCH_NR_ERROR_ACTIONS
|
||||||
? error_actions[CACHE_SET_ERROR_ACTION(sb)]
|
? bch_error_actions[CACHE_SET_ERROR_ACTION(sb)]
|
||||||
: "unknown",
|
: "unknown",
|
||||||
|
|
||||||
CACHE_SET_CLEAN(sb),
|
CACHE_SET_CLEAN(sb),
|
||||||
@ -313,19 +241,19 @@ void bcache_super_print(struct cache_sb *sb, int units)
|
|||||||
CACHE_SET_DATA_REPLICAS_WANT(sb),
|
CACHE_SET_DATA_REPLICAS_WANT(sb),
|
||||||
|
|
||||||
CACHE_SET_META_PREFERRED_CSUM_TYPE(sb) < BCH_CSUM_NR
|
CACHE_SET_META_PREFERRED_CSUM_TYPE(sb) < BCH_CSUM_NR
|
||||||
? csum_types[CACHE_SET_META_PREFERRED_CSUM_TYPE(sb)]
|
? bch_csum_types[CACHE_SET_META_PREFERRED_CSUM_TYPE(sb)]
|
||||||
: "unknown",
|
: "unknown",
|
||||||
|
|
||||||
CACHE_SET_DATA_PREFERRED_CSUM_TYPE(sb) < BCH_CSUM_NR
|
CACHE_SET_DATA_PREFERRED_CSUM_TYPE(sb) < BCH_CSUM_NR
|
||||||
? csum_types[CACHE_SET_DATA_PREFERRED_CSUM_TYPE(sb)]
|
? bch_csum_types[CACHE_SET_DATA_PREFERRED_CSUM_TYPE(sb)]
|
||||||
: "unknown",
|
: "unknown",
|
||||||
|
|
||||||
CACHE_SET_COMPRESSION_TYPE(sb) < BCH_COMPRESSION_NR
|
CACHE_SET_COMPRESSION_TYPE(sb) < BCH_COMPRESSION_NR
|
||||||
? compression_types[CACHE_SET_COMPRESSION_TYPE(sb)]
|
? bch_compression_types[CACHE_SET_COMPRESSION_TYPE(sb)]
|
||||||
: "unknown",
|
: "unknown",
|
||||||
|
|
||||||
CACHE_SET_STR_HASH_TYPE(sb) < BCH_STR_HASH_NR
|
CACHE_SET_STR_HASH_TYPE(sb) < BCH_STR_HASH_NR
|
||||||
? str_hash_types[CACHE_SET_STR_HASH_TYPE(sb)]
|
? bch_str_hash_types[CACHE_SET_STR_HASH_TYPE(sb)]
|
||||||
: "unknown",
|
: "unknown",
|
||||||
|
|
||||||
CACHE_INODE_32BIT(sb),
|
CACHE_INODE_32BIT(sb),
|
||||||
@ -356,14 +284,14 @@ void bcache_super_print(struct cache_sb *sb, int units)
|
|||||||
" Discard: %llu\n",
|
" Discard: %llu\n",
|
||||||
i, member_uuid_str,
|
i, member_uuid_str,
|
||||||
pr_units(le16_to_cpu(m->bucket_size) *
|
pr_units(le16_to_cpu(m->bucket_size) *
|
||||||
le64_to_cpu(m->nbuckets), units).b,
|
le64_to_cpu(m->nbuckets), units),
|
||||||
pr_units(le16_to_cpu(m->bucket_size), units).b,
|
pr_units(le16_to_cpu(m->bucket_size), units),
|
||||||
le16_to_cpu(m->first_bucket),
|
le16_to_cpu(m->first_bucket),
|
||||||
le64_to_cpu(m->nbuckets),
|
le64_to_cpu(m->nbuckets),
|
||||||
last_mount ? ctime(&last_mount) : "(never)",
|
last_mount ? ctime(&last_mount) : "(never)",
|
||||||
|
|
||||||
CACHE_STATE(m) < CACHE_STATE_NR
|
CACHE_STATE(m) < CACHE_STATE_NR
|
||||||
? member_states[CACHE_STATE(m)]
|
? bch_cache_state[CACHE_STATE(m)]
|
||||||
: "unknown",
|
: "unknown",
|
||||||
|
|
||||||
CACHE_TIER(m),
|
CACHE_TIER(m),
|
||||||
@ -371,7 +299,7 @@ void bcache_super_print(struct cache_sb *sb, int units)
|
|||||||
CACHE_HAS_DATA(m),
|
CACHE_HAS_DATA(m),
|
||||||
|
|
||||||
CACHE_REPLACEMENT(m) < CACHE_REPLACEMENT_NR
|
CACHE_REPLACEMENT(m) < CACHE_REPLACEMENT_NR
|
||||||
? replacement_policies[CACHE_REPLACEMENT(m)]
|
? bch_cache_replacement_policies[CACHE_REPLACEMENT(m)]
|
||||||
: "unknown",
|
: "unknown",
|
||||||
|
|
||||||
CACHE_DISCARD(m));
|
CACHE_DISCARD(m));
|
||||||
@ -387,8 +315,7 @@ struct cache_sb *bcache_super_read(const char *path)
|
|||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
die("couldn't open %s", path);
|
die("couldn't open %s", path);
|
||||||
|
|
||||||
if (pread(fd, &sb, sizeof(sb), SB_SECTOR << 9) != sizeof(sb))
|
xpread(fd, &sb, sizeof(sb), SB_SECTOR << 9);
|
||||||
die("error reading superblock");
|
|
||||||
|
|
||||||
if (memcmp(&sb.magic, &BCACHE_MAGIC, sizeof(sb.magic)))
|
if (memcmp(&sb.magic, &BCACHE_MAGIC, sizeof(sb.magic)))
|
||||||
die("not a bcache superblock");
|
die("not a bcache superblock");
|
||||||
@ -397,8 +324,7 @@ struct cache_sb *bcache_super_read(const char *path)
|
|||||||
|
|
||||||
ret = calloc(1, bytes);
|
ret = calloc(1, bytes);
|
||||||
|
|
||||||
if (pread(fd, ret, bytes, SB_SECTOR << 9) != bytes)
|
xpread(fd, ret, bytes, SB_SECTOR << 9);
|
||||||
die("error reading superblock");
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
21
libbcache.h
21
libbcache.h
@ -1,17 +1,20 @@
|
|||||||
#ifndef _LIBBCACHE_H
|
#ifndef _LIBBCACHE_H
|
||||||
#define _LIBBCACHE_H
|
#define _LIBBCACHE_H
|
||||||
|
|
||||||
#include "tools-util.h"
|
#include <linux/uuid.h>
|
||||||
#include "stdbool.h"
|
#include "stdbool.h"
|
||||||
|
|
||||||
extern const char * const cache_state[];
|
#include "tools-util.h"
|
||||||
extern const char * const replacement_policies[];
|
|
||||||
extern const char * const csum_types[];
|
struct cache_sb;
|
||||||
extern const char * const compression_types[];
|
|
||||||
extern const char * const str_hash_types[];
|
enum fsck_err_opts {
|
||||||
extern const char * const error_actions[];
|
FSCK_ERR_ASK,
|
||||||
extern const char * const bdev_cache_mode[];
|
FSCK_ERR_YES,
|
||||||
extern const char * const bdev_state[];
|
FSCK_ERR_NO,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern enum fsck_err_opts fsck_err_opt;
|
||||||
|
|
||||||
struct dev_opts {
|
struct dev_opts {
|
||||||
int fd;
|
int fd;
|
||||||
|
@ -254,6 +254,9 @@ static int bch_prio_write(struct cache *ca)
|
|||||||
bool need_new_journal_entry;
|
bool need_new_journal_entry;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
|
if (c->opts.nochanges)
|
||||||
|
return 0;
|
||||||
|
|
||||||
trace_bcache_prio_write_start(ca);
|
trace_bcache_prio_write_start(ca);
|
||||||
|
|
||||||
atomic64_add(ca->mi.bucket_size * prio_buckets(ca),
|
atomic64_add(ca->mi.bucket_size * prio_buckets(ca),
|
||||||
|
@ -210,8 +210,9 @@
|
|||||||
#define bch_meta_write_fault(name) \
|
#define bch_meta_write_fault(name) \
|
||||||
dynamic_fault("bcache:meta:write:" name)
|
dynamic_fault("bcache:meta:write:" name)
|
||||||
|
|
||||||
#define bch_fmt(_c, fmt) \
|
#ifndef bch_fmt
|
||||||
"bcache (%s): " fmt "\n", ((_c)->name)
|
#define bch_fmt(_c, fmt) "bcache (%s): " fmt "\n", ((_c)->name)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define bch_info(c, fmt, ...) \
|
#define bch_info(c, fmt, ...) \
|
||||||
printk(KERN_INFO bch_fmt(c, fmt), ##__VA_ARGS__)
|
printk(KERN_INFO bch_fmt(c, fmt), ##__VA_ARGS__)
|
||||||
|
@ -89,6 +89,16 @@ void bkey_debugcheck(struct cache_set *c, struct btree *b, struct bkey_s_c k)
|
|||||||
ops->key_debugcheck(c, b, k);
|
ops->key_debugcheck(c, b, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bch_val_to_text(struct cache_set *c, enum bkey_type type,
|
||||||
|
char *buf, size_t size, struct bkey_s_c k)
|
||||||
|
{
|
||||||
|
const struct bkey_ops *ops = bch_bkey_ops[type];
|
||||||
|
|
||||||
|
if (k.k->type >= KEY_TYPE_GENERIC_NR &&
|
||||||
|
ops->val_to_text)
|
||||||
|
ops->val_to_text(c, buf, size, k);
|
||||||
|
}
|
||||||
|
|
||||||
void bch_bkey_val_to_text(struct cache_set *c, enum bkey_type type,
|
void bch_bkey_val_to_text(struct cache_set *c, enum bkey_type type,
|
||||||
char *buf, size_t size, struct bkey_s_c k)
|
char *buf, size_t size, struct bkey_s_c k)
|
||||||
{
|
{
|
||||||
|
@ -67,6 +67,8 @@ const char *btree_bkey_invalid(struct cache_set *, struct btree *,
|
|||||||
struct bkey_s_c);
|
struct bkey_s_c);
|
||||||
|
|
||||||
void bkey_debugcheck(struct cache_set *, struct btree *, struct bkey_s_c);
|
void bkey_debugcheck(struct cache_set *, struct btree *, struct bkey_s_c);
|
||||||
|
void bch_val_to_text(struct cache_set *, enum bkey_type,
|
||||||
|
char *, size_t, struct bkey_s_c);
|
||||||
void bch_bkey_val_to_text(struct cache_set *, enum bkey_type,
|
void bch_bkey_val_to_text(struct cache_set *, enum bkey_type,
|
||||||
char *, size_t, struct bkey_s_c);
|
char *, size_t, struct bkey_s_c);
|
||||||
|
|
||||||
|
@ -149,7 +149,8 @@ static int mca_reap_notrace(struct cache_set *c, struct btree *b, bool flush)
|
|||||||
if (!six_trylock_write(&b->lock))
|
if (!six_trylock_write(&b->lock))
|
||||||
goto out_unlock_intent;
|
goto out_unlock_intent;
|
||||||
|
|
||||||
if (btree_node_write_error(b))
|
if (btree_node_write_error(b) ||
|
||||||
|
btree_node_noevict(b))
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
if (!list_empty(&b->write_blocked))
|
if (!list_empty(&b->write_blocked))
|
||||||
@ -699,3 +700,56 @@ retry:
|
|||||||
|
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bch_print_btree_node(struct cache_set *c, struct btree *b,
|
||||||
|
char *buf, size_t len)
|
||||||
|
{
|
||||||
|
const struct bkey_format *f = &b->format;
|
||||||
|
struct bset_stats stats;
|
||||||
|
char ptrs[100];
|
||||||
|
|
||||||
|
memset(&stats, 0, sizeof(stats));
|
||||||
|
|
||||||
|
bch_val_to_text(c, BKEY_TYPE_BTREE, ptrs, sizeof(ptrs),
|
||||||
|
bkey_i_to_s_c(&b->key));
|
||||||
|
bch_btree_keys_stats(b, &stats);
|
||||||
|
|
||||||
|
return scnprintf(buf, len,
|
||||||
|
"l %u %llu:%llu - %llu:%llu:\n"
|
||||||
|
" ptrs: %s\n"
|
||||||
|
" format: u64s %u fields %u %u %u %u %u\n"
|
||||||
|
" unpack fn len: %u\n"
|
||||||
|
" bytes used %zu/%zu (%zu%% full)\n"
|
||||||
|
" sib u64s: %u, %u (merge threshold %zu)\n"
|
||||||
|
" nr packed keys %u\n"
|
||||||
|
" nr unpacked keys %u\n"
|
||||||
|
" floats %zu\n"
|
||||||
|
" failed unpacked %zu\n"
|
||||||
|
" failed prev %zu\n"
|
||||||
|
" failed overflow %zu\n",
|
||||||
|
b->level,
|
||||||
|
b->data->min_key.inode,
|
||||||
|
b->data->min_key.offset,
|
||||||
|
b->data->max_key.inode,
|
||||||
|
b->data->max_key.offset,
|
||||||
|
ptrs,
|
||||||
|
f->key_u64s,
|
||||||
|
f->bits_per_field[0],
|
||||||
|
f->bits_per_field[1],
|
||||||
|
f->bits_per_field[2],
|
||||||
|
f->bits_per_field[3],
|
||||||
|
f->bits_per_field[4],
|
||||||
|
b->unpack_fn_len,
|
||||||
|
b->nr.live_u64s * sizeof(u64),
|
||||||
|
btree_bytes(c) - sizeof(struct btree_node),
|
||||||
|
b->nr.live_u64s * 100 / btree_max_u64s(c),
|
||||||
|
b->sib_u64s[0],
|
||||||
|
b->sib_u64s[1],
|
||||||
|
BTREE_FOREGROUND_MERGE_THRESHOLD(c),
|
||||||
|
b->nr.packed_keys,
|
||||||
|
b->nr.unpacked_keys,
|
||||||
|
stats.floats,
|
||||||
|
stats.failed_unpacked,
|
||||||
|
stats.failed_prev,
|
||||||
|
stats.failed_overflow);
|
||||||
|
}
|
||||||
|
@ -56,6 +56,16 @@ static inline unsigned btree_blocks(struct cache_set *c)
|
|||||||
return c->sb.btree_node_size >> c->block_bits;
|
return c->sb.btree_node_size >> c->block_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define BTREE_SPLIT_THRESHOLD(c) (btree_blocks(c) * 3 / 4)
|
||||||
|
|
||||||
|
#define BTREE_FOREGROUND_MERGE_THRESHOLD(c) (btree_max_u64s(c) * 1 / 3)
|
||||||
|
#define BTREE_FOREGROUND_MERGE_HYSTERESIS(c) \
|
||||||
|
(BTREE_FOREGROUND_MERGE_THRESHOLD(c) + \
|
||||||
|
(BTREE_FOREGROUND_MERGE_THRESHOLD(c) << 2))
|
||||||
|
|
||||||
#define btree_node_root(_c, _b) ((_c)->btree_roots[(_b)->btree_id].b)
|
#define btree_node_root(_c, _b) ((_c)->btree_roots[(_b)->btree_id].b)
|
||||||
|
|
||||||
|
int bch_print_btree_node(struct cache_set *, struct btree *,
|
||||||
|
char *, size_t);
|
||||||
|
|
||||||
#endif /* _BCACHE_BTREE_CACHE_H */
|
#endif /* _BCACHE_BTREE_CACHE_H */
|
||||||
|
@ -200,7 +200,7 @@ static unsigned sort_extent_whiteouts(struct bkey_packed *dst,
|
|||||||
const struct bkey_format *f = &iter->b->format;
|
const struct bkey_format *f = &iter->b->format;
|
||||||
struct bkey_packed *in, *out = dst;
|
struct bkey_packed *in, *out = dst;
|
||||||
struct bkey_i l, r;
|
struct bkey_i l, r;
|
||||||
bool prev = false, l_packed;
|
bool prev = false, l_packed = false;
|
||||||
u64 max_packed_size = bkey_field_max(f, BKEY_FIELD_SIZE);
|
u64 max_packed_size = bkey_field_max(f, BKEY_FIELD_SIZE);
|
||||||
u64 max_packed_offset = bkey_field_max(f, BKEY_FIELD_OFFSET);
|
u64 max_packed_offset = bkey_field_max(f, BKEY_FIELD_OFFSET);
|
||||||
u64 new_size;
|
u64 new_size;
|
||||||
@ -1443,8 +1443,9 @@ void __bch_btree_node_write(struct cache_set *c, struct btree *b,
|
|||||||
* Make sure to update b->written so bch_btree_init_next() doesn't
|
* Make sure to update b->written so bch_btree_init_next() doesn't
|
||||||
* break:
|
* break:
|
||||||
*/
|
*/
|
||||||
if (bch_journal_error(&c->journal)) {
|
if (bch_journal_error(&c->journal) ||
|
||||||
set_btree_node_write_error(b);
|
c->opts.nochanges) {
|
||||||
|
set_btree_node_noevict(b);
|
||||||
b->written += sectors_to_write;
|
b->written += sectors_to_write;
|
||||||
|
|
||||||
btree_bounce_free(c, order, used_mempool, data);
|
btree_bounce_free(c, order, used_mempool, data);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define _BCACHE_BTREE_TYPES_H
|
#define _BCACHE_BTREE_TYPES_H
|
||||||
|
|
||||||
#include <linux/bcache.h>
|
#include <linux/bcache.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/rhashtable.h>
|
#include <linux/rhashtable.h>
|
||||||
#include <linux/semaphore.h>
|
#include <linux/semaphore.h>
|
||||||
@ -138,6 +139,7 @@ enum btree_flags {
|
|||||||
BTREE_NODE_read_error,
|
BTREE_NODE_read_error,
|
||||||
BTREE_NODE_write_error,
|
BTREE_NODE_write_error,
|
||||||
BTREE_NODE_dirty,
|
BTREE_NODE_dirty,
|
||||||
|
BTREE_NODE_noevict,
|
||||||
BTREE_NODE_write_idx,
|
BTREE_NODE_write_idx,
|
||||||
BTREE_NODE_accessed,
|
BTREE_NODE_accessed,
|
||||||
BTREE_NODE_write_in_flight,
|
BTREE_NODE_write_in_flight,
|
||||||
@ -147,6 +149,7 @@ enum btree_flags {
|
|||||||
BTREE_FLAG(read_error);
|
BTREE_FLAG(read_error);
|
||||||
BTREE_FLAG(write_error);
|
BTREE_FLAG(write_error);
|
||||||
BTREE_FLAG(dirty);
|
BTREE_FLAG(dirty);
|
||||||
|
BTREE_FLAG(noevict);
|
||||||
BTREE_FLAG(write_idx);
|
BTREE_FLAG(write_idx);
|
||||||
BTREE_FLAG(accessed);
|
BTREE_FLAG(accessed);
|
||||||
BTREE_FLAG(write_in_flight);
|
BTREE_FLAG(write_in_flight);
|
||||||
|
@ -11,13 +11,6 @@ struct bkey_format_state;
|
|||||||
struct bkey_format;
|
struct bkey_format;
|
||||||
struct btree;
|
struct btree;
|
||||||
|
|
||||||
#define BTREE_SPLIT_THRESHOLD(c) (btree_blocks(c) * 3 / 4)
|
|
||||||
|
|
||||||
#define BTREE_FOREGROUND_MERGE_THRESHOLD(c) (btree_max_u64s(c) * 1 / 3)
|
|
||||||
#define BTREE_FOREGROUND_MERGE_HYSTERESIS(c) \
|
|
||||||
(BTREE_FOREGROUND_MERGE_THRESHOLD(c) + \
|
|
||||||
(BTREE_FOREGROUND_MERGE_THRESHOLD(c) << 2))
|
|
||||||
|
|
||||||
static inline void btree_node_reset_sib_u64s(struct btree *b)
|
static inline void btree_node_reset_sib_u64s(struct btree *b)
|
||||||
{
|
{
|
||||||
b->sib_u64s[0] = b->nr.live_u64s;
|
b->sib_u64s[0] = b->nr.live_u64s;
|
||||||
|
@ -46,6 +46,9 @@ void __bch_btree_verify(struct cache_set *c, struct btree *b)
|
|||||||
struct bio *bio;
|
struct bio *bio;
|
||||||
struct closure cl;
|
struct closure cl;
|
||||||
|
|
||||||
|
if (c->opts.nochanges)
|
||||||
|
return;
|
||||||
|
|
||||||
closure_init_stack(&cl);
|
closure_init_stack(&cl);
|
||||||
|
|
||||||
btree_node_io_lock(b);
|
btree_node_io_lock(b);
|
||||||
@ -296,55 +299,6 @@ static const struct file_operations btree_debug_ops = {
|
|||||||
.read = bch_read_btree,
|
.read = bch_read_btree,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int print_btree_node(struct dump_iter *i, struct btree *b)
|
|
||||||
{
|
|
||||||
const struct bkey_format *f = &b->format;
|
|
||||||
struct bset_stats stats;
|
|
||||||
|
|
||||||
memset(&stats, 0, sizeof(stats));
|
|
||||||
|
|
||||||
bch_btree_keys_stats(b, &stats);
|
|
||||||
|
|
||||||
i->bytes = scnprintf(i->buf, sizeof(i->buf),
|
|
||||||
"l %u %llu:%llu - %llu:%llu:\n"
|
|
||||||
" format: u64s %u fields %u %u %u %u %u\n"
|
|
||||||
" unpack fn len: %u\n"
|
|
||||||
" bytes used %zu/%zu (%zu%% full)\n"
|
|
||||||
" sib u64s: %u, %u (merge threshold %zu)\n"
|
|
||||||
" nr packed keys %u\n"
|
|
||||||
" nr unpacked keys %u\n"
|
|
||||||
" floats %zu\n"
|
|
||||||
" failed unpacked %zu\n"
|
|
||||||
" failed prev %zu\n"
|
|
||||||
" failed overflow %zu\n",
|
|
||||||
b->level,
|
|
||||||
b->data->min_key.inode,
|
|
||||||
b->data->min_key.offset,
|
|
||||||
b->data->max_key.inode,
|
|
||||||
b->data->max_key.offset,
|
|
||||||
f->key_u64s,
|
|
||||||
f->bits_per_field[0],
|
|
||||||
f->bits_per_field[1],
|
|
||||||
f->bits_per_field[2],
|
|
||||||
f->bits_per_field[3],
|
|
||||||
f->bits_per_field[4],
|
|
||||||
b->unpack_fn_len,
|
|
||||||
b->nr.live_u64s * sizeof(u64),
|
|
||||||
btree_bytes(i->c) - sizeof(struct btree_node),
|
|
||||||
b->nr.live_u64s * 100 / btree_max_u64s(i->c),
|
|
||||||
b->sib_u64s[0],
|
|
||||||
b->sib_u64s[1],
|
|
||||||
BTREE_FOREGROUND_MERGE_THRESHOLD(i->c),
|
|
||||||
b->nr.packed_keys,
|
|
||||||
b->nr.unpacked_keys,
|
|
||||||
stats.floats,
|
|
||||||
stats.failed_unpacked,
|
|
||||||
stats.failed_prev,
|
|
||||||
stats.failed_overflow);
|
|
||||||
|
|
||||||
return flush_buf(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t bch_read_btree_formats(struct file *file, char __user *buf,
|
static ssize_t bch_read_btree_formats(struct file *file, char __user *buf,
|
||||||
size_t size, loff_t *ppos)
|
size_t size, loff_t *ppos)
|
||||||
{
|
{
|
||||||
@ -365,7 +319,9 @@ static ssize_t bch_read_btree_formats(struct file *file, char __user *buf,
|
|||||||
return i->ret;
|
return i->ret;
|
||||||
|
|
||||||
for_each_btree_node(&iter, i->c, i->id, i->from, 0, b) {
|
for_each_btree_node(&iter, i->c, i->id, i->from, 0, b) {
|
||||||
err = print_btree_node(i, b);
|
i->bytes = bch_print_btree_node(i->c, b, i->buf,
|
||||||
|
sizeof(i->buf));
|
||||||
|
err = flush_buf(i);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -421,7 +377,9 @@ static ssize_t bch_read_bfloat_failed(struct file *file, char __user *buf,
|
|||||||
struct bkey_packed *_k = bch_btree_node_iter_peek(node_iter, b);
|
struct bkey_packed *_k = bch_btree_node_iter_peek(node_iter, b);
|
||||||
|
|
||||||
if (iter.nodes[0] != prev_node) {
|
if (iter.nodes[0] != prev_node) {
|
||||||
err = print_btree_node(i, iter.nodes[0]);
|
i->bytes = bch_print_btree_node(i->c, b, i->buf,
|
||||||
|
sizeof(i->buf));
|
||||||
|
err = flush_buf(i);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
#include <linux/dcache.h>
|
#include <linux/dcache.h>
|
||||||
|
|
||||||
static unsigned dirent_name_bytes(struct bkey_s_c_dirent d)
|
unsigned bch_dirent_name_bytes(struct bkey_s_c_dirent d)
|
||||||
{
|
{
|
||||||
unsigned len = bkey_val_bytes(d.k) - sizeof(struct bch_dirent);
|
unsigned len = bkey_val_bytes(d.k) - sizeof(struct bch_dirent);
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ static u64 dirent_hash_key(const struct bch_hash_info *info, const void *key)
|
|||||||
static u64 dirent_hash_bkey(const struct bch_hash_info *info, struct bkey_s_c k)
|
static u64 dirent_hash_bkey(const struct bch_hash_info *info, struct bkey_s_c k)
|
||||||
{
|
{
|
||||||
struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
|
struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
|
||||||
struct qstr name = QSTR_INIT(d.v->d_name, dirent_name_bytes(d));
|
struct qstr name = QSTR_INIT(d.v->d_name, bch_dirent_name_bytes(d));
|
||||||
|
|
||||||
return bch_dirent_hash(info, &name);
|
return bch_dirent_hash(info, &name);
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ static u64 dirent_hash_bkey(const struct bch_hash_info *info, struct bkey_s_c k)
|
|||||||
static bool dirent_cmp_key(struct bkey_s_c _l, const void *_r)
|
static bool dirent_cmp_key(struct bkey_s_c _l, const void *_r)
|
||||||
{
|
{
|
||||||
struct bkey_s_c_dirent l = bkey_s_c_to_dirent(_l);
|
struct bkey_s_c_dirent l = bkey_s_c_to_dirent(_l);
|
||||||
int len = dirent_name_bytes(l);
|
int len = bch_dirent_name_bytes(l);
|
||||||
const struct qstr *r = _r;
|
const struct qstr *r = _r;
|
||||||
|
|
||||||
return len - r->len ?: memcmp(l.v->d_name, r->name, len);
|
return len - r->len ?: memcmp(l.v->d_name, r->name, len);
|
||||||
@ -79,8 +79,8 @@ static bool dirent_cmp_bkey(struct bkey_s_c _l, struct bkey_s_c _r)
|
|||||||
{
|
{
|
||||||
struct bkey_s_c_dirent l = bkey_s_c_to_dirent(_l);
|
struct bkey_s_c_dirent l = bkey_s_c_to_dirent(_l);
|
||||||
struct bkey_s_c_dirent r = bkey_s_c_to_dirent(_r);
|
struct bkey_s_c_dirent r = bkey_s_c_to_dirent(_r);
|
||||||
int l_len = dirent_name_bytes(l);
|
int l_len = bch_dirent_name_bytes(l);
|
||||||
int r_len = dirent_name_bytes(r);
|
int r_len = bch_dirent_name_bytes(r);
|
||||||
|
|
||||||
return l_len - r_len ?: memcmp(l.v->d_name, r.v->d_name, l_len);
|
return l_len - r_len ?: memcmp(l.v->d_name, r.v->d_name, l_len);
|
||||||
}
|
}
|
||||||
@ -125,7 +125,7 @@ static void bch_dirent_to_text(struct cache_set *c, char *buf,
|
|||||||
|
|
||||||
if (size) {
|
if (size) {
|
||||||
unsigned n = min_t(unsigned, size,
|
unsigned n = min_t(unsigned, size,
|
||||||
dirent_name_bytes(d));
|
bch_dirent_name_bytes(d));
|
||||||
memcpy(buf, d.v->d_name, n);
|
memcpy(buf, d.v->d_name, n);
|
||||||
buf[size - 1] = '\0';
|
buf[size - 1] = '\0';
|
||||||
buf += n;
|
buf += n;
|
||||||
@ -167,15 +167,16 @@ static struct bkey_i_dirent *dirent_create_key(u8 type,
|
|||||||
bkey_val_bytes(&dirent->k) -
|
bkey_val_bytes(&dirent->k) -
|
||||||
(sizeof(struct bch_dirent) + name->len));
|
(sizeof(struct bch_dirent) + name->len));
|
||||||
|
|
||||||
EBUG_ON(dirent_name_bytes(dirent_i_to_s_c(dirent)) != name->len);
|
EBUG_ON(bch_dirent_name_bytes(dirent_i_to_s_c(dirent)) != name->len);
|
||||||
|
|
||||||
return dirent;
|
return dirent;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bch_dirent_create(struct cache_set *c, struct inode *dir, u8 type,
|
int bch_dirent_create(struct cache_set *c, u64 dir_inum,
|
||||||
const struct qstr *name, u64 dst_inum)
|
const struct bch_hash_info *hash_info,
|
||||||
|
u8 type, const struct qstr *name, u64 dst_inum,
|
||||||
|
u64 *journal_seq, int flags)
|
||||||
{
|
{
|
||||||
struct bch_inode_info *ei = to_bch_ei(dir);
|
|
||||||
struct bkey_i_dirent *dirent;
|
struct bkey_i_dirent *dirent;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -183,9 +184,8 @@ int bch_dirent_create(struct cache_set *c, struct inode *dir, u8 type,
|
|||||||
if (!dirent)
|
if (!dirent)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = bch_hash_set(dirent_hash_desc, &ei->str_hash, c,
|
ret = bch_hash_set(dirent_hash_desc, hash_info, c, dir_inum,
|
||||||
ei->vfs_inode.i_ino, &ei->journal_seq,
|
journal_seq, &dirent->k_i, flags);
|
||||||
&dirent->k_i, BCH_HASH_SET_MUST_CREATE);
|
|
||||||
kfree(dirent);
|
kfree(dirent);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -346,26 +346,25 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bch_dirent_delete(struct cache_set *c, struct inode *dir,
|
int bch_dirent_delete(struct cache_set *c, u64 dir_inum,
|
||||||
const struct qstr *name)
|
const struct bch_hash_info *hash_info,
|
||||||
|
const struct qstr *name,
|
||||||
|
u64 *journal_seq)
|
||||||
{
|
{
|
||||||
struct bch_inode_info *ei = to_bch_ei(dir);
|
return bch_hash_delete(dirent_hash_desc, hash_info,
|
||||||
|
c, dir_inum, journal_seq, name);
|
||||||
return bch_hash_delete(dirent_hash_desc, &ei->str_hash,
|
|
||||||
c, ei->vfs_inode.i_ino,
|
|
||||||
&ei->journal_seq, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 bch_dirent_lookup(struct cache_set *c, struct inode *dir,
|
u64 bch_dirent_lookup(struct cache_set *c, u64 dir_inum,
|
||||||
|
const struct bch_hash_info *hash_info,
|
||||||
const struct qstr *name)
|
const struct qstr *name)
|
||||||
{
|
{
|
||||||
struct bch_inode_info *ei = to_bch_ei(dir);
|
|
||||||
struct btree_iter iter;
|
struct btree_iter iter;
|
||||||
struct bkey_s_c k;
|
struct bkey_s_c k;
|
||||||
u64 inum;
|
u64 inum;
|
||||||
|
|
||||||
k = bch_hash_lookup(dirent_hash_desc, &ei->str_hash, c,
|
k = bch_hash_lookup(dirent_hash_desc, hash_info, c,
|
||||||
ei->vfs_inode.i_ino, &iter, name);
|
dir_inum, &iter, name);
|
||||||
if (IS_ERR(k.k)) {
|
if (IS_ERR(k.k)) {
|
||||||
bch_btree_iter_unlock(&iter);
|
bch_btree_iter_unlock(&iter);
|
||||||
return 0;
|
return 0;
|
||||||
@ -428,7 +427,7 @@ int bch_readdir(struct cache_set *c, struct file *file,
|
|||||||
if (k.k->p.inode > inode->i_ino)
|
if (k.k->p.inode > inode->i_ino)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
len = dirent_name_bytes(dirent);
|
len = bch_dirent_name_bytes(dirent);
|
||||||
|
|
||||||
pr_debug("emitting %s", dirent.v->d_name);
|
pr_debug("emitting %s", dirent.v->d_name);
|
||||||
|
|
||||||
|
@ -7,10 +7,13 @@ struct qstr;
|
|||||||
struct file;
|
struct file;
|
||||||
struct dir_context;
|
struct dir_context;
|
||||||
struct cache_set;
|
struct cache_set;
|
||||||
|
struct bch_hash_info;
|
||||||
|
|
||||||
int bch_dirent_create(struct cache_set *c, struct inode *, u8,
|
unsigned bch_dirent_name_bytes(struct bkey_s_c_dirent);
|
||||||
const struct qstr *, u64);
|
int bch_dirent_create(struct cache_set *c, u64, const struct bch_hash_info *,
|
||||||
int bch_dirent_delete(struct cache_set *c, struct inode *, const struct qstr *);
|
u8, const struct qstr *, u64, u64 *, int);
|
||||||
|
int bch_dirent_delete(struct cache_set *, u64, const struct bch_hash_info *,
|
||||||
|
const struct qstr *, u64 *);
|
||||||
|
|
||||||
enum bch_rename_mode {
|
enum bch_rename_mode {
|
||||||
BCH_RENAME,
|
BCH_RENAME,
|
||||||
@ -23,8 +26,9 @@ int bch_dirent_rename(struct cache_set *,
|
|||||||
struct inode *, const struct qstr *,
|
struct inode *, const struct qstr *,
|
||||||
u64 *, enum bch_rename_mode);
|
u64 *, enum bch_rename_mode);
|
||||||
|
|
||||||
u64 bch_dirent_lookup(struct cache_set *c, struct inode *,
|
u64 bch_dirent_lookup(struct cache_set *, u64, const struct bch_hash_info *,
|
||||||
const struct qstr *);
|
const struct qstr *);
|
||||||
|
|
||||||
int bch_empty_dir(struct cache_set *, u64);
|
int bch_empty_dir(struct cache_set *, u64);
|
||||||
int bch_readdir(struct cache_set *, struct file *, struct dir_context *);
|
int bch_readdir(struct cache_set *, struct file *, struct dir_context *);
|
||||||
|
|
||||||
|
@ -101,38 +101,51 @@ enum {
|
|||||||
BCH_FSCK_UNKNOWN_VERSION = 4,
|
BCH_FSCK_UNKNOWN_VERSION = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define unfixable_fsck_err(c, msg, ...) \
|
/* These macros return true if error should be fixed: */
|
||||||
do { \
|
|
||||||
bch_err(c, msg " (repair unimplemented)", ##__VA_ARGS__); \
|
|
||||||
ret = BCH_FSCK_REPAIR_UNIMPLEMENTED; \
|
|
||||||
goto fsck_err; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define unfixable_fsck_err_on(cond, c, ...) \
|
/* XXX: mark in superblock that filesystem contains errors, if we ignore: */
|
||||||
do { \
|
|
||||||
if (cond) \
|
|
||||||
unfixable_fsck_err(c, __VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define fsck_err(c, msg, ...) \
|
#ifndef __fsck_err
|
||||||
do { \
|
#define __fsck_err(c, _can_fix, _can_ignore, _nofix_msg, msg, ...) \
|
||||||
if (!(c)->opts.fix_errors) { \
|
({ \
|
||||||
bch_err(c, msg, ##__VA_ARGS__); \
|
bool _fix = false; \
|
||||||
|
\
|
||||||
|
if (_can_fix && (c)->opts.fix_errors) { \
|
||||||
|
bch_err(c, msg ", fixing", ##__VA_ARGS__); \
|
||||||
|
set_bit(CACHE_SET_FSCK_FIXED_ERRORS, &(c)->flags); \
|
||||||
|
_fix = true; \
|
||||||
|
} else if (_can_ignore && \
|
||||||
|
(c)->opts.errors == BCH_ON_ERROR_CONTINUE) { \
|
||||||
|
bch_err(c, msg " (ignoring)", ##__VA_ARGS__); \
|
||||||
|
} else { \
|
||||||
|
bch_err(c, msg " ("_nofix_msg")", ##__VA_ARGS__); \
|
||||||
ret = BCH_FSCK_ERRORS_NOT_FIXED; \
|
ret = BCH_FSCK_ERRORS_NOT_FIXED; \
|
||||||
goto fsck_err; \
|
goto fsck_err; \
|
||||||
} \
|
} \
|
||||||
set_bit(CACHE_SET_FSCK_FIXED_ERRORS, &(c)->flags); \
|
\
|
||||||
bch_err(c, msg ", fixing", ##__VA_ARGS__); \
|
BUG_ON(!_fix && !_can_ignore); \
|
||||||
} while (0)
|
_fix; \
|
||||||
|
})
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __fsck_err_on(cond, c, _can_fix, _can_ignore, _nofix_msg, ...) \
|
||||||
|
((cond) ? __fsck_err(c, _can_fix, _can_ignore, \
|
||||||
|
_nofix_msg, ##__VA_ARGS__) : false)
|
||||||
|
|
||||||
|
#define unfixable_fsck_err_on(cond, c, ...) \
|
||||||
|
__fsck_err_on(cond, c, false, true, "repair unimplemented", ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define need_fsck_err_on(cond, c, ...) \
|
||||||
|
__fsck_err_on(cond, c, false, true, "run fsck to correct", ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define mustfix_fsck_err(c, ...) \
|
||||||
|
__fsck_err(c, true, false, "not fixing", ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define mustfix_fsck_err_on(cond, c, ...) \
|
||||||
|
__fsck_err_on(cond, c, true, false, "not fixing", ##__VA_ARGS__)
|
||||||
|
|
||||||
#define fsck_err_on(cond, c, ...) \
|
#define fsck_err_on(cond, c, ...) \
|
||||||
({ \
|
__fsck_err_on(cond, c, true, true, "not fixing", ##__VA_ARGS__)
|
||||||
bool _ret = (cond); \
|
|
||||||
\
|
|
||||||
if (_ret) \
|
|
||||||
fsck_err(c, __VA_ARGS__); \
|
|
||||||
_ret; \
|
|
||||||
})
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fatal errors: these don't indicate a bug, but we can't continue running in RW
|
* Fatal errors: these don't indicate a bug, but we can't continue running in RW
|
||||||
|
@ -108,15 +108,16 @@ struct btree_nr_keys bch_key_sort_fix_overlapping(struct bset *dst,
|
|||||||
|
|
||||||
/* Common among btree and extent ptrs */
|
/* Common among btree and extent ptrs */
|
||||||
|
|
||||||
bool bch_extent_has_device(struct bkey_s_c_extent e, unsigned dev)
|
const struct bch_extent_ptr *
|
||||||
|
bch_extent_has_device(struct bkey_s_c_extent e, unsigned dev)
|
||||||
{
|
{
|
||||||
const struct bch_extent_ptr *ptr;
|
const struct bch_extent_ptr *ptr;
|
||||||
|
|
||||||
extent_for_each_ptr(e, ptr)
|
extent_for_each_ptr(e, ptr)
|
||||||
if (ptr->dev == dev)
|
if (ptr->dev == dev)
|
||||||
return true;
|
return ptr;
|
||||||
|
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned bch_extent_nr_ptrs_from(struct bkey_s_c_extent e,
|
unsigned bch_extent_nr_ptrs_from(struct bkey_s_c_extent e,
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
#ifndef _BCACHE_EXTENTS_H
|
#ifndef _BCACHE_EXTENTS_H
|
||||||
#define _BCACHE_EXTENTS_H
|
#define _BCACHE_EXTENTS_H
|
||||||
|
|
||||||
|
#include "bcache.h"
|
||||||
#include "bkey.h"
|
#include "bkey.h"
|
||||||
|
|
||||||
#include <linux/bcache.h>
|
#include <linux/bcache.h>
|
||||||
|
|
||||||
struct bch_replace_info;
|
struct btree_node_iter;
|
||||||
union bch_extent_crc;
|
|
||||||
struct btree_iter;
|
|
||||||
struct btree_insert;
|
struct btree_insert;
|
||||||
struct btree_insert_entry;
|
struct btree_insert_entry;
|
||||||
|
struct extent_insert_hook;
|
||||||
|
|
||||||
struct btree_nr_keys bch_key_sort_fix_overlapping(struct bset *,
|
struct btree_nr_keys bch_key_sort_fix_overlapping(struct bset *,
|
||||||
struct btree *,
|
struct btree *,
|
||||||
@ -485,7 +485,8 @@ static inline void bch_extent_drop_ptr(struct bkey_s_extent e,
|
|||||||
bch_extent_drop_redundant_crcs(e);
|
bch_extent_drop_redundant_crcs(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bch_extent_has_device(struct bkey_s_c_extent, unsigned);
|
const struct bch_extent_ptr *
|
||||||
|
bch_extent_has_device(struct bkey_s_c_extent, unsigned);
|
||||||
|
|
||||||
bool bch_cut_front(struct bpos, struct bkey_i *);
|
bool bch_cut_front(struct bpos, struct bkey_i *);
|
||||||
bool bch_cut_back(struct bpos, struct bkey *);
|
bool bch_cut_back(struct bpos, struct bkey *);
|
||||||
|
@ -11,6 +11,529 @@
|
|||||||
|
|
||||||
#include <linux/generic-radix-tree.h>
|
#include <linux/generic-radix-tree.h>
|
||||||
|
|
||||||
|
#define QSTR(n) { { { .len = strlen(n) } }, .name = n }
|
||||||
|
|
||||||
|
static int remove_dirent(struct cache_set *c, struct btree_iter *iter,
|
||||||
|
struct bkey_s_c_dirent dirent)
|
||||||
|
{
|
||||||
|
struct qstr name;
|
||||||
|
struct bkey_i_inode dir_inode;
|
||||||
|
struct bch_hash_info dir_hash_info;
|
||||||
|
u64 dir_inum = dirent.k->p.inode;
|
||||||
|
int ret;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
name.len = bch_dirent_name_bytes(dirent);
|
||||||
|
buf = kmalloc(name.len + 1, GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
memcpy(buf, dirent.v->d_name, name.len);
|
||||||
|
buf[name.len] = '\0';
|
||||||
|
name.name = buf;
|
||||||
|
|
||||||
|
/* Unlock iter so we don't deadlock, after copying name: */
|
||||||
|
bch_btree_iter_unlock(iter);
|
||||||
|
|
||||||
|
ret = bch_inode_find_by_inum(c, dir_inum, &dir_inode);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
dir_hash_info = bch_hash_info_init(&dir_inode.v);
|
||||||
|
|
||||||
|
ret = bch_dirent_delete(c, dir_inum, &dir_hash_info, &name, NULL);
|
||||||
|
err:
|
||||||
|
kfree(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reattach_inode(struct cache_set *c,
|
||||||
|
struct bkey_i_inode *lostfound_inode,
|
||||||
|
u64 inum)
|
||||||
|
{
|
||||||
|
struct bch_hash_info lostfound_hash_info =
|
||||||
|
bch_hash_info_init(&lostfound_inode->v);
|
||||||
|
char name_buf[20];
|
||||||
|
struct qstr name;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
snprintf(name_buf, sizeof(name_buf), "%llu", inum);
|
||||||
|
name = (struct qstr) QSTR(name_buf);
|
||||||
|
|
||||||
|
le32_add_cpu(&lostfound_inode->v.i_nlink, 1);
|
||||||
|
|
||||||
|
ret = bch_btree_insert(c, BTREE_ID_INODES, &lostfound_inode->k_i,
|
||||||
|
NULL, NULL, NULL, 0);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return bch_dirent_create(c, lostfound_inode->k.p.inode,
|
||||||
|
&lostfound_hash_info,
|
||||||
|
DT_DIR, &name, inum, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct inode_walker {
|
||||||
|
bool first_this_inode;
|
||||||
|
bool have_inode;
|
||||||
|
u16 i_mode;
|
||||||
|
u64 i_size;
|
||||||
|
u64 cur_inum;
|
||||||
|
struct bkey_i_inode inode;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct inode_walker inode_walker_init(void)
|
||||||
|
{
|
||||||
|
return (struct inode_walker) {
|
||||||
|
.cur_inum = -1,
|
||||||
|
.have_inode = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static int walk_inode(struct cache_set *c, struct inode_walker *w, u64 inum)
|
||||||
|
{
|
||||||
|
w->first_this_inode = inum != w->cur_inum;
|
||||||
|
w->cur_inum = inum;
|
||||||
|
|
||||||
|
if (w->first_this_inode) {
|
||||||
|
int ret = bch_inode_find_by_inum(c, inum, &w->inode);
|
||||||
|
|
||||||
|
if (ret && ret != -ENOENT)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
w->have_inode = !ret;
|
||||||
|
|
||||||
|
if (w->have_inode) {
|
||||||
|
w->i_mode = le16_to_cpu(w->inode.v.i_mode);
|
||||||
|
w->i_size = le64_to_cpu(w->inode.v.i_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk extents: verify that extents have a corresponding S_ISREG inode, and
|
||||||
|
* that i_size an i_sectors are consistent
|
||||||
|
*/
|
||||||
|
noinline_for_stack
|
||||||
|
static int check_extents(struct cache_set *c)
|
||||||
|
{
|
||||||
|
struct inode_walker w = inode_walker_init();
|
||||||
|
struct btree_iter iter;
|
||||||
|
struct bkey_s_c k;
|
||||||
|
u64 i_sectors;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
for_each_btree_key(&iter, c, BTREE_ID_EXTENTS,
|
||||||
|
POS(BCACHE_ROOT_INO, 0), k) {
|
||||||
|
if (k.k->type == KEY_TYPE_DISCARD)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = walk_inode(c, &w, k.k->p.inode);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
|
||||||
|
unfixable_fsck_err_on(!w.have_inode, c,
|
||||||
|
"extent type %u for missing inode %llu",
|
||||||
|
k.k->type, k.k->p.inode);
|
||||||
|
|
||||||
|
unfixable_fsck_err_on(w.first_this_inode && w.have_inode &&
|
||||||
|
le64_to_cpu(w.inode.v.i_sectors) !=
|
||||||
|
(i_sectors = bch_count_inode_sectors(c, w.cur_inum)),
|
||||||
|
c, "i_sectors wrong: got %llu, should be %llu",
|
||||||
|
le64_to_cpu(w.inode.v.i_sectors), i_sectors);
|
||||||
|
|
||||||
|
unfixable_fsck_err_on(w.have_inode &&
|
||||||
|
!S_ISREG(w.i_mode) && !S_ISLNK(w.i_mode), c,
|
||||||
|
"extent type %u for non regular file, inode %llu mode %o",
|
||||||
|
k.k->type, k.k->p.inode, w.i_mode);
|
||||||
|
|
||||||
|
unfixable_fsck_err_on(k.k->type != BCH_RESERVATION &&
|
||||||
|
k.k->p.offset > round_up(w.i_size, PAGE_SIZE) >> 9, c,
|
||||||
|
"extent type %u offset %llu past end of inode %llu, i_size %llu",
|
||||||
|
k.k->type, k.k->p.offset, k.k->p.inode, w.i_size);
|
||||||
|
}
|
||||||
|
fsck_err:
|
||||||
|
return bch_btree_iter_unlock(&iter) ?: ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk dirents: verify that they all have a corresponding S_ISDIR inode,
|
||||||
|
* validate d_type
|
||||||
|
*/
|
||||||
|
noinline_for_stack
|
||||||
|
static int check_dirents(struct cache_set *c)
|
||||||
|
{
|
||||||
|
struct inode_walker w = inode_walker_init();
|
||||||
|
struct btree_iter iter;
|
||||||
|
struct bkey_s_c k;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
for_each_btree_key(&iter, c, BTREE_ID_DIRENTS,
|
||||||
|
POS(BCACHE_ROOT_INO, 0), k) {
|
||||||
|
struct bkey_s_c_dirent d;
|
||||||
|
struct bkey_i_inode target;
|
||||||
|
bool have_target;
|
||||||
|
u64 d_inum;
|
||||||
|
|
||||||
|
ret = walk_inode(c, &w, k.k->p.inode);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
|
||||||
|
unfixable_fsck_err_on(!w.have_inode, c,
|
||||||
|
"dirent in nonexisting directory %llu",
|
||||||
|
k.k->p.inode);
|
||||||
|
|
||||||
|
unfixable_fsck_err_on(!S_ISDIR(w.i_mode), c,
|
||||||
|
"dirent in non directory inode %llu, type %u",
|
||||||
|
k.k->p.inode, mode_to_type(w.i_mode));
|
||||||
|
|
||||||
|
if (k.k->type != BCH_DIRENT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
d = bkey_s_c_to_dirent(k);
|
||||||
|
d_inum = le64_to_cpu(d.v->d_inum);
|
||||||
|
|
||||||
|
if (fsck_err_on(d_inum == d.k->p.inode, c,
|
||||||
|
"dirent points to own directory")) {
|
||||||
|
ret = remove_dirent(c, &iter, d);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bch_inode_find_by_inum(c, d_inum, &target);
|
||||||
|
if (ret && ret != -ENOENT)
|
||||||
|
break;
|
||||||
|
|
||||||
|
have_target = !ret;
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
if (fsck_err_on(!have_target, c,
|
||||||
|
"dirent points to missing inode %llu, type %u filename %s",
|
||||||
|
d_inum, d.v->d_type, d.v->d_name)) {
|
||||||
|
ret = remove_dirent(c, &iter, d);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fsck_err_on(have_target &&
|
||||||
|
d.v->d_type !=
|
||||||
|
mode_to_type(le16_to_cpu(target.v.i_mode)), c,
|
||||||
|
"incorrect d_type: got %u should be %u, filename %s",
|
||||||
|
d.v->d_type,
|
||||||
|
mode_to_type(le16_to_cpu(target.v.i_mode)),
|
||||||
|
d.v->d_name)) {
|
||||||
|
struct bkey_i_dirent *n;
|
||||||
|
|
||||||
|
n = kmalloc(bkey_bytes(d.k), GFP_KERNEL);
|
||||||
|
if (!n) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bkey_reassemble(&n->k_i, d.s_c);
|
||||||
|
n->v.d_type = mode_to_type(le16_to_cpu(target.v.i_mode));
|
||||||
|
|
||||||
|
ret = bch_btree_insert_at(c, NULL, NULL, NULL,
|
||||||
|
BTREE_INSERT_NOFAIL,
|
||||||
|
BTREE_INSERT_ENTRY(&iter, &n->k_i));
|
||||||
|
kfree(n);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err:
|
||||||
|
fsck_err:
|
||||||
|
return bch_btree_iter_unlock(&iter) ?: ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk xattrs: verify that they all have a corresponding inode
|
||||||
|
*/
|
||||||
|
noinline_for_stack
|
||||||
|
static int check_xattrs(struct cache_set *c)
|
||||||
|
{
|
||||||
|
struct inode_walker w = inode_walker_init();
|
||||||
|
struct btree_iter iter;
|
||||||
|
struct bkey_s_c k;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
for_each_btree_key(&iter, c, BTREE_ID_XATTRS,
|
||||||
|
POS(BCACHE_ROOT_INO, 0), k) {
|
||||||
|
ret = walk_inode(c, &w, k.k->p.inode);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
|
||||||
|
unfixable_fsck_err_on(!w.have_inode, c,
|
||||||
|
"xattr for missing inode %llu",
|
||||||
|
k.k->p.inode);
|
||||||
|
}
|
||||||
|
fsck_err:
|
||||||
|
return bch_btree_iter_unlock(&iter) ?: ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get root directory, create if it doesn't exist: */
|
||||||
|
static int check_root(struct cache_set *c, struct bkey_i_inode *root_inode)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = bch_inode_find_by_inum(c, BCACHE_ROOT_INO, root_inode);
|
||||||
|
if (ret && ret != -ENOENT)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (fsck_err_on(ret, c, "root directory missing"))
|
||||||
|
goto create_root;
|
||||||
|
|
||||||
|
if (fsck_err_on(!S_ISDIR(le16_to_cpu(root_inode->v.i_mode)), c,
|
||||||
|
"root inode not a directory"))
|
||||||
|
goto create_root;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
fsck_err:
|
||||||
|
return ret;
|
||||||
|
create_root:
|
||||||
|
bch_inode_init(c, root_inode, 0, 0, S_IFDIR|S_IRWXU|S_IRUGO|S_IXUGO, 0);
|
||||||
|
root_inode->k.p.inode = BCACHE_ROOT_INO;
|
||||||
|
|
||||||
|
return bch_btree_insert(c, BTREE_ID_INODES, &root_inode->k_i,
|
||||||
|
NULL, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get lost+found, create if it doesn't exist: */
|
||||||
|
static int check_lostfound(struct cache_set *c,
|
||||||
|
struct bkey_i_inode *root_inode,
|
||||||
|
struct bkey_i_inode *lostfound_inode)
|
||||||
|
{
|
||||||
|
struct qstr lostfound = QSTR("lost+found");
|
||||||
|
struct bch_hash_info root_hash_info = bch_hash_info_init(&root_inode->v);
|
||||||
|
u64 inum;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
inum = bch_dirent_lookup(c, BCACHE_ROOT_INO, &root_hash_info,
|
||||||
|
&lostfound);
|
||||||
|
if (!inum) {
|
||||||
|
bch_notice(c, "creating lost+found");
|
||||||
|
goto create_lostfound;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bch_inode_find_by_inum(c, inum, lostfound_inode);
|
||||||
|
if (ret && ret != -ENOENT)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (fsck_err_on(ret, c, "lost+found missing"))
|
||||||
|
goto create_lostfound;
|
||||||
|
|
||||||
|
if (fsck_err_on(!S_ISDIR(le16_to_cpu(lostfound_inode->v.i_mode)), c,
|
||||||
|
"lost+found inode not a directory"))
|
||||||
|
goto create_lostfound;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
fsck_err:
|
||||||
|
return ret;
|
||||||
|
create_lostfound:
|
||||||
|
le32_add_cpu(&root_inode->v.i_nlink, 1);
|
||||||
|
|
||||||
|
ret = bch_btree_insert(c, BTREE_ID_INODES, &root_inode->k_i,
|
||||||
|
NULL, NULL, NULL, 0);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
bch_inode_init(c, lostfound_inode, 0, 0, S_IFDIR|S_IRWXU|S_IRUGO|S_IXUGO, 0);
|
||||||
|
|
||||||
|
ret = bch_inode_create(c, &lostfound_inode->k_i, BLOCKDEV_INODE_MAX, 0,
|
||||||
|
&c->unused_inode_hint);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = bch_dirent_create(c, BCACHE_ROOT_INO, &root_hash_info, DT_DIR,
|
||||||
|
&lostfound, lostfound_inode->k.p.inode, NULL, 0);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct inode_bitmap {
|
||||||
|
unsigned long *bits;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool inode_bitmap_test(struct inode_bitmap *b, size_t nr)
|
||||||
|
{
|
||||||
|
return nr < b->size ? test_bit(nr, b->bits) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int inode_bitmap_set(struct inode_bitmap *b, size_t nr)
|
||||||
|
{
|
||||||
|
if (nr >= b->size) {
|
||||||
|
size_t new_size = max(max(PAGE_SIZE * 8,
|
||||||
|
b->size * 2),
|
||||||
|
nr + 1);
|
||||||
|
void *n;
|
||||||
|
|
||||||
|
new_size = roundup_pow_of_two(new_size);
|
||||||
|
n = krealloc(b->bits, new_size / 8, GFP_KERNEL|__GFP_ZERO);
|
||||||
|
if (!n)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
b->bits = n;
|
||||||
|
b->size = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
__set_bit(nr, b->bits);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pathbuf {
|
||||||
|
size_t nr;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
struct pathbuf_entry {
|
||||||
|
u64 inum;
|
||||||
|
u64 offset;
|
||||||
|
} *entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int path_down(struct pathbuf *p, u64 inum)
|
||||||
|
{
|
||||||
|
if (p->nr == p->size) {
|
||||||
|
size_t new_size = max(256UL, p->size * 2);
|
||||||
|
void *n = krealloc(p->entries,
|
||||||
|
new_size * sizeof(p->entries[0]),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!n)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
p->entries = n;
|
||||||
|
p->size = new_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
p->entries[p->nr++] = (struct pathbuf_entry) {
|
||||||
|
.inum = inum,
|
||||||
|
.offset = 0,
|
||||||
|
};
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
noinline_for_stack
|
||||||
|
static int check_directory_structure(struct cache_set *c,
|
||||||
|
struct bkey_i_inode *lostfound_inode)
|
||||||
|
{
|
||||||
|
struct inode_bitmap dirs_done = { NULL, 0 };
|
||||||
|
struct pathbuf path = { 0, 0, NULL };
|
||||||
|
struct pathbuf_entry *e;
|
||||||
|
struct btree_iter iter;
|
||||||
|
struct bkey_s_c k;
|
||||||
|
struct bkey_s_c_dirent dirent;
|
||||||
|
bool had_unreachable;
|
||||||
|
u64 d_inum;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* DFS: */
|
||||||
|
restart_dfs:
|
||||||
|
ret = inode_bitmap_set(&dirs_done, BCACHE_ROOT_INO);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
ret = path_down(&path, BCACHE_ROOT_INO);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
while (path.nr) {
|
||||||
|
next:
|
||||||
|
e = &path.entries[path.nr - 1];
|
||||||
|
|
||||||
|
if (e->offset == U64_MAX)
|
||||||
|
goto up;
|
||||||
|
|
||||||
|
for_each_btree_key(&iter, c, BTREE_ID_DIRENTS,
|
||||||
|
POS(e->inum, e->offset + 1), k) {
|
||||||
|
if (k.k->p.inode != e->inum)
|
||||||
|
break;
|
||||||
|
|
||||||
|
e->offset = k.k->p.offset;
|
||||||
|
|
||||||
|
if (k.k->type != BCH_DIRENT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dirent = bkey_s_c_to_dirent(k);
|
||||||
|
|
||||||
|
if (dirent.v->d_type != DT_DIR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
d_inum = le64_to_cpu(dirent.v->d_inum);
|
||||||
|
|
||||||
|
if (fsck_err_on(inode_bitmap_test(&dirs_done, d_inum), c,
|
||||||
|
"directory with multiple hardlinks")) {
|
||||||
|
ret = remove_dirent(c, &iter, dirent);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = inode_bitmap_set(&dirs_done, d_inum);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
ret = path_down(&path, d_inum);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
bch_btree_iter_unlock(&iter);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
ret = bch_btree_iter_unlock(&iter);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
up:
|
||||||
|
path.nr--;
|
||||||
|
}
|
||||||
|
|
||||||
|
had_unreachable = false;
|
||||||
|
|
||||||
|
for_each_btree_key(&iter, c, BTREE_ID_INODES, POS_MIN, k) {
|
||||||
|
if (k.k->type != BCH_INODE_FS ||
|
||||||
|
!S_ISDIR(le16_to_cpu(bkey_s_c_to_inode(k).v->i_mode)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (fsck_err_on(!inode_bitmap_test(&dirs_done, k.k->p.inode), c,
|
||||||
|
"unreachable directory found (inum %llu)",
|
||||||
|
k.k->p.inode)) {
|
||||||
|
bch_btree_iter_unlock(&iter);
|
||||||
|
|
||||||
|
ret = reattach_inode(c, lostfound_inode, k.k->p.inode);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
had_unreachable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = bch_btree_iter_unlock(&iter);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (had_unreachable) {
|
||||||
|
bch_info(c, "reattached unreachable directories, restarting pass to check for loops");
|
||||||
|
kfree(dirs_done.bits);
|
||||||
|
kfree(path.entries);
|
||||||
|
memset(&dirs_done, 0, sizeof(dirs_done));
|
||||||
|
memset(&path, 0, sizeof(path));
|
||||||
|
goto restart_dfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
kfree(dirs_done.bits);
|
||||||
|
kfree(path.entries);
|
||||||
|
return ret;
|
||||||
|
err:
|
||||||
|
fsck_err:
|
||||||
|
ret = bch_btree_iter_unlock(&iter) ?: ret;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
struct nlink {
|
struct nlink {
|
||||||
u32 count;
|
u32 count;
|
||||||
u32 dir_count;
|
u32 dir_count;
|
||||||
@ -40,11 +563,6 @@ static void inc_link(struct cache_set *c, struct nlinks *links,
|
|||||||
link->count++;
|
link->count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* XXX: should do a DFS (via filesystem heirarchy), and make sure all dirents
|
|
||||||
* are reachable
|
|
||||||
*/
|
|
||||||
|
|
||||||
noinline_for_stack
|
noinline_for_stack
|
||||||
static int bch_gc_walk_dirents(struct cache_set *c, struct nlinks *links,
|
static int bch_gc_walk_dirents(struct cache_set *c, struct nlinks *links,
|
||||||
u64 range_start, u64 *range_end)
|
u64 range_start, u64 *range_end)
|
||||||
@ -99,7 +617,9 @@ s64 bch_count_inode_sectors(struct cache_set *c, u64 inum)
|
|||||||
return bch_btree_iter_unlock(&iter) ?: sectors;
|
return bch_btree_iter_unlock(&iter) ?: sectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bch_gc_do_inode(struct cache_set *c, struct btree_iter *iter,
|
static int bch_gc_do_inode(struct cache_set *c,
|
||||||
|
struct bkey_i_inode *lostfound_inode,
|
||||||
|
struct btree_iter *iter,
|
||||||
struct bkey_s_c_inode inode, struct nlink link)
|
struct bkey_s_c_inode inode, struct nlink link)
|
||||||
{
|
{
|
||||||
u16 i_mode = le16_to_cpu(inode.v->i_mode);
|
u16 i_mode = le16_to_cpu(inode.v->i_mode);
|
||||||
@ -115,14 +635,15 @@ static int bch_gc_do_inode(struct cache_set *c, struct btree_iter *iter,
|
|||||||
inode.k->p.inode, i_nlink,
|
inode.k->p.inode, i_nlink,
|
||||||
link.count, mode_to_type(i_mode));
|
link.count, mode_to_type(i_mode));
|
||||||
|
|
||||||
|
/* These should have been caught/fixed by earlier passes: */
|
||||||
if (S_ISDIR(i_mode)) {
|
if (S_ISDIR(i_mode)) {
|
||||||
unfixable_fsck_err_on(link.count > 1, c,
|
need_fsck_err_on(link.count > 1, c,
|
||||||
"directory %llu with multiple hardlinks: %u",
|
"directory %llu with multiple hardlinks: %u",
|
||||||
inode.k->p.inode, link.count);
|
inode.k->p.inode, link.count);
|
||||||
|
|
||||||
real_i_nlink = link.count * 2 + link.dir_count;
|
real_i_nlink = link.count * 2 + link.dir_count;
|
||||||
} else {
|
} else {
|
||||||
unfixable_fsck_err_on(link.dir_count, c,
|
need_fsck_err_on(link.dir_count, c,
|
||||||
"found dirents for non directory %llu",
|
"found dirents for non directory %llu",
|
||||||
inode.k->p.inode);
|
inode.k->p.inode);
|
||||||
|
|
||||||
@ -135,11 +656,16 @@ static int bch_gc_do_inode(struct cache_set *c, struct btree_iter *iter,
|
|||||||
"but found orphaned inode %llu",
|
"but found orphaned inode %llu",
|
||||||
inode.k->p.inode);
|
inode.k->p.inode);
|
||||||
|
|
||||||
unfixable_fsck_err_on(S_ISDIR(i_mode) &&
|
if (fsck_err_on(S_ISDIR(i_mode) &&
|
||||||
bch_empty_dir(c, inode.k->p.inode), c,
|
bch_empty_dir(c, inode.k->p.inode), c,
|
||||||
"non empty directory with link count 0, "
|
"non empty directory with link count 0, "
|
||||||
"inode nlink %u, dir links found %u",
|
"inode nlink %u, dir links found %u",
|
||||||
i_nlink, link.dir_count);
|
i_nlink, link.dir_count)) {
|
||||||
|
ret = reattach_inode(c, lostfound_inode,
|
||||||
|
inode.k->p.inode);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bch_verbose(c, "deleting inode %llu", inode.k->p.inode);
|
bch_verbose(c, "deleting inode %llu", inode.k->p.inode);
|
||||||
|
|
||||||
@ -235,7 +761,9 @@ fsck_err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
noinline_for_stack
|
noinline_for_stack
|
||||||
static int bch_gc_walk_inodes(struct cache_set *c, struct nlinks *links,
|
static int bch_gc_walk_inodes(struct cache_set *c,
|
||||||
|
struct bkey_i_inode *lostfound_inode,
|
||||||
|
struct nlinks *links,
|
||||||
u64 range_start, u64 range_end)
|
u64 range_start, u64 range_end)
|
||||||
{
|
{
|
||||||
struct btree_iter iter;
|
struct btree_iter iter;
|
||||||
@ -257,7 +785,8 @@ peek_nlinks: link = genradix_iter_peek(&nlinks_iter, links);
|
|||||||
|
|
||||||
nlinks_pos = range_start + nlinks_iter.pos;
|
nlinks_pos = range_start + nlinks_iter.pos;
|
||||||
if (iter.pos.inode > nlinks_pos) {
|
if (iter.pos.inode > nlinks_pos) {
|
||||||
unfixable_fsck_err_on(link && link->count, c,
|
/* Should have been caught by dirents pass: */
|
||||||
|
need_fsck_err_on(link && link->count, c,
|
||||||
"missing inode %llu (nlink %u)",
|
"missing inode %llu (nlink %u)",
|
||||||
nlinks_pos, link->count);
|
nlinks_pos, link->count);
|
||||||
genradix_iter_advance(&nlinks_iter, links);
|
genradix_iter_advance(&nlinks_iter, links);
|
||||||
@ -274,9 +803,8 @@ peek_nlinks: link = genradix_iter_peek(&nlinks_iter, links);
|
|||||||
*/
|
*/
|
||||||
bch_btree_iter_unlock(&iter);
|
bch_btree_iter_unlock(&iter);
|
||||||
|
|
||||||
ret = bch_gc_do_inode(c, &iter,
|
ret = bch_gc_do_inode(c, lostfound_inode, &iter,
|
||||||
bkey_s_c_to_inode(k),
|
bkey_s_c_to_inode(k), *link);
|
||||||
*link);
|
|
||||||
if (ret == -EINTR)
|
if (ret == -EINTR)
|
||||||
continue;
|
continue;
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -285,7 +813,8 @@ peek_nlinks: link = genradix_iter_peek(&nlinks_iter, links);
|
|||||||
if (link->count)
|
if (link->count)
|
||||||
atomic_long_inc(&c->nr_inodes);
|
atomic_long_inc(&c->nr_inodes);
|
||||||
} else {
|
} else {
|
||||||
unfixable_fsck_err_on(link->count, c,
|
/* Should have been caught by dirents pass: */
|
||||||
|
need_fsck_err_on(link->count, c,
|
||||||
"missing inode %llu (nlink %u)",
|
"missing inode %llu (nlink %u)",
|
||||||
nlinks_pos, link->count);
|
nlinks_pos, link->count);
|
||||||
}
|
}
|
||||||
@ -304,7 +833,9 @@ fsck_err:
|
|||||||
return ret ?: ret2;
|
return ret ?: ret2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bch_gc_inode_nlinks(struct cache_set *c)
|
noinline_for_stack
|
||||||
|
static int check_inode_nlinks(struct cache_set *c,
|
||||||
|
struct bkey_i_inode *lostfound_inode)
|
||||||
{
|
{
|
||||||
struct nlinks links;
|
struct nlinks links;
|
||||||
u64 this_iter_range_start, next_iter_range_start = 0;
|
u64 this_iter_range_start, next_iter_range_start = 0;
|
||||||
@ -322,7 +853,7 @@ int bch_gc_inode_nlinks(struct cache_set *c)
|
|||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ret = bch_gc_walk_inodes(c, &links,
|
ret = bch_gc_walk_inodes(c, lostfound_inode, &links,
|
||||||
this_iter_range_start,
|
this_iter_range_start,
|
||||||
next_iter_range_start);
|
next_iter_range_start);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -336,140 +867,45 @@ int bch_gc_inode_nlinks(struct cache_set *c)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void next_inode(struct cache_set *c, u64 inum, u64 *cur_inum,
|
|
||||||
struct bkey_i_inode *inode,
|
|
||||||
bool *first_this_inode, bool *have_inode,
|
|
||||||
u64 *i_size, u16 *i_mode)
|
|
||||||
{
|
|
||||||
*first_this_inode = inum != *cur_inum;
|
|
||||||
*cur_inum = inum;
|
|
||||||
|
|
||||||
if (*first_this_inode) {
|
|
||||||
*have_inode = !bch_inode_find_by_inum(c, inum, inode);
|
|
||||||
|
|
||||||
if (*have_inode) {
|
|
||||||
*i_mode = le16_to_cpu(inode->v.i_mode);
|
|
||||||
*i_size = le64_to_cpu(inode->v.i_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks for inconsistencies that shouldn't happen, unless we have a bug.
|
* Checks for inconsistencies that shouldn't happen, unless we have a bug.
|
||||||
* Doesn't fix them yet, mainly because they haven't yet been observed:
|
* Doesn't fix them yet, mainly because they haven't yet been observed:
|
||||||
*/
|
*/
|
||||||
int bch_fsck(struct cache_set *c)
|
int bch_fsck(struct cache_set *c, bool full_fsck)
|
||||||
{
|
{
|
||||||
struct btree_iter iter;
|
struct bkey_i_inode root_inode, lostfound_inode;
|
||||||
struct bkey_s_c k;
|
int ret;
|
||||||
struct bkey_i_inode inode;
|
|
||||||
bool first_this_inode, have_inode;
|
|
||||||
u64 cur_inum, i_sectors;
|
|
||||||
u64 i_size = 0;
|
|
||||||
u16 i_mode = 0;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
cur_inum = -1;
|
ret = check_root(c, &root_inode);
|
||||||
have_inode = false;
|
|
||||||
for_each_btree_key(&iter, c, BTREE_ID_EXTENTS,
|
|
||||||
POS(BCACHE_ROOT_INO, 0), k) {
|
|
||||||
if (k.k->type == KEY_TYPE_DISCARD)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
next_inode(c, k.k->p.inode, &cur_inum, &inode,
|
|
||||||
&first_this_inode, &have_inode,
|
|
||||||
&i_size, &i_mode);
|
|
||||||
|
|
||||||
unfixable_fsck_err_on(!have_inode, c,
|
|
||||||
"extent type %u for missing inode %llu",
|
|
||||||
k.k->type, k.k->p.inode);
|
|
||||||
|
|
||||||
unfixable_fsck_err_on(first_this_inode && have_inode &&
|
|
||||||
le64_to_cpu(inode.v.i_sectors) !=
|
|
||||||
(i_sectors = bch_count_inode_sectors(c, cur_inum)),
|
|
||||||
c, "i_sectors wrong: got %llu, should be %llu",
|
|
||||||
le64_to_cpu(inode.v.i_sectors), i_sectors);
|
|
||||||
|
|
||||||
unfixable_fsck_err_on(have_inode &&
|
|
||||||
!S_ISREG(i_mode) && !S_ISLNK(i_mode), c,
|
|
||||||
"extent type %u for non regular file, inode %llu mode %o",
|
|
||||||
k.k->type, k.k->p.inode, i_mode);
|
|
||||||
|
|
||||||
unfixable_fsck_err_on(k.k->type != BCH_RESERVATION &&
|
|
||||||
k.k->p.offset > round_up(i_size, PAGE_SIZE) >> 9, c,
|
|
||||||
"extent type %u offset %llu past end of inode %llu, i_size %llu",
|
|
||||||
k.k->type, k.k->p.offset, k.k->p.inode, i_size);
|
|
||||||
}
|
|
||||||
ret = bch_btree_iter_unlock(&iter);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
cur_inum = -1;
|
ret = check_lostfound(c, &root_inode, &lostfound_inode);
|
||||||
have_inode = false;
|
|
||||||
for_each_btree_key(&iter, c, BTREE_ID_DIRENTS,
|
|
||||||
POS(BCACHE_ROOT_INO, 0), k) {
|
|
||||||
struct bkey_s_c_dirent d;
|
|
||||||
struct bkey_i_inode target;
|
|
||||||
bool have_target;
|
|
||||||
u64 d_inum;
|
|
||||||
|
|
||||||
next_inode(c, k.k->p.inode, &cur_inum, &inode,
|
|
||||||
&first_this_inode, &have_inode,
|
|
||||||
&i_size, &i_mode);
|
|
||||||
|
|
||||||
unfixable_fsck_err_on(!have_inode, c,
|
|
||||||
"dirent in nonexisting directory %llu",
|
|
||||||
k.k->p.inode);
|
|
||||||
|
|
||||||
unfixable_fsck_err_on(!S_ISDIR(i_mode), c,
|
|
||||||
"dirent in non directory inode %llu, type %u",
|
|
||||||
k.k->p.inode, mode_to_type(i_mode));
|
|
||||||
|
|
||||||
if (k.k->type != BCH_DIRENT)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
d = bkey_s_c_to_dirent(k);
|
|
||||||
d_inum = le64_to_cpu(d.v->d_inum);
|
|
||||||
|
|
||||||
unfixable_fsck_err_on(d_inum == d.k->p.inode, c,
|
|
||||||
"dirent points to own directory");
|
|
||||||
|
|
||||||
have_target = !bch_inode_find_by_inum(c, d_inum, &target);
|
|
||||||
|
|
||||||
unfixable_fsck_err_on(!have_target, c,
|
|
||||||
"dirent points to missing inode %llu, type %u filename %s",
|
|
||||||
d_inum, d.v->d_type, d.v->d_name);
|
|
||||||
|
|
||||||
unfixable_fsck_err_on(have_target &&
|
|
||||||
d.v->d_type !=
|
|
||||||
mode_to_type(le16_to_cpu(target.v.i_mode)), c,
|
|
||||||
"incorrect d_type: got %u should be %u, filename %s",
|
|
||||||
d.v->d_type,
|
|
||||||
mode_to_type(le16_to_cpu(target.v.i_mode)),
|
|
||||||
d.v->d_name);
|
|
||||||
}
|
|
||||||
ret = bch_btree_iter_unlock(&iter);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
cur_inum = -1;
|
if (!full_fsck)
|
||||||
have_inode = false;
|
goto check_nlinks;
|
||||||
for_each_btree_key(&iter, c, BTREE_ID_XATTRS,
|
|
||||||
POS(BCACHE_ROOT_INO, 0), k) {
|
|
||||||
next_inode(c, k.k->p.inode, &cur_inum, &inode,
|
|
||||||
&first_this_inode, &have_inode,
|
|
||||||
&i_size, &i_mode);
|
|
||||||
|
|
||||||
unfixable_fsck_err_on(!have_inode, c,
|
ret = check_extents(c);
|
||||||
"xattr for missing inode %llu",
|
if (ret)
|
||||||
k.k->p.inode);
|
return ret;
|
||||||
}
|
|
||||||
ret = bch_btree_iter_unlock(&iter);
|
ret = check_dirents(c);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = check_xattrs(c);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = check_directory_structure(c, &lostfound_inode);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
check_nlinks:
|
||||||
|
ret = check_inode_nlinks(c, &lostfound_inode);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
fsck_err:
|
|
||||||
bch_btree_iter_unlock(&iter);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#define _BCACHE_FS_GC_H
|
#define _BCACHE_FS_GC_H
|
||||||
|
|
||||||
s64 bch_count_inode_sectors(struct cache_set *, u64);
|
s64 bch_count_inode_sectors(struct cache_set *, u64);
|
||||||
int bch_gc_inode_nlinks(struct cache_set *);
|
int bch_fsck(struct cache_set *, bool);
|
||||||
int bch_fsck(struct cache_set *);
|
|
||||||
|
|
||||||
#endif /* _BCACHE_FS_GC_H */
|
#endif /* _BCACHE_FS_GC_H */
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
static struct kmem_cache *bch_inode_cache;
|
static struct kmem_cache *bch_inode_cache;
|
||||||
|
|
||||||
static void bch_inode_init(struct bch_inode_info *, struct bkey_s_c_inode);
|
static void bch_vfs_inode_init(struct bch_inode_info *, struct bkey_s_c_inode);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* I_SIZE_DIRTY requires special handling:
|
* I_SIZE_DIRTY requires special handling:
|
||||||
@ -175,7 +175,7 @@ static struct inode *bch_vfs_inode_get(struct super_block *sb, u64 inum)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ei = to_bch_ei(inode);
|
ei = to_bch_ei(inode);
|
||||||
bch_inode_init(ei, bkey_s_c_to_inode(k));
|
bch_vfs_inode_init(ei, bkey_s_c_to_inode(k));
|
||||||
|
|
||||||
ei->journal_seq = bch_inode_journal_seq(&c->journal, inum);
|
ei->journal_seq = bch_inode_journal_seq(&c->journal, inum);
|
||||||
|
|
||||||
@ -193,10 +193,7 @@ static struct inode *bch_vfs_inode_create(struct cache_set *c,
|
|||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct posix_acl *default_acl = NULL, *acl = NULL;
|
struct posix_acl *default_acl = NULL, *acl = NULL;
|
||||||
struct bch_inode_info *ei;
|
struct bch_inode_info *ei;
|
||||||
struct bch_inode *bi;
|
|
||||||
struct bkey_i_inode bkey_inode;
|
struct bkey_i_inode bkey_inode;
|
||||||
struct timespec ts = CURRENT_TIME;
|
|
||||||
s64 now = timespec_to_ns(&ts);
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
inode = new_inode(parent->i_sb);
|
inode = new_inode(parent->i_sb);
|
||||||
@ -213,19 +210,8 @@ static struct inode *bch_vfs_inode_create(struct cache_set *c,
|
|||||||
|
|
||||||
ei = to_bch_ei(inode);
|
ei = to_bch_ei(inode);
|
||||||
|
|
||||||
bi = &bkey_inode_init(&bkey_inode.k_i)->v;
|
bch_inode_init(c, &bkey_inode, i_uid_read(inode),
|
||||||
bi->i_uid = cpu_to_le32(i_uid_read(inode));
|
i_gid_read(inode), inode->i_mode, rdev);
|
||||||
bi->i_gid = cpu_to_le32(i_gid_read(inode));
|
|
||||||
|
|
||||||
bi->i_mode = cpu_to_le16(inode->i_mode);
|
|
||||||
bi->i_dev = cpu_to_le32(rdev);
|
|
||||||
bi->i_atime = cpu_to_le64(now);
|
|
||||||
bi->i_mtime = cpu_to_le64(now);
|
|
||||||
bi->i_ctime = cpu_to_le64(now);
|
|
||||||
bi->i_nlink = cpu_to_le32(S_ISDIR(mode) ? 2 : 1);
|
|
||||||
|
|
||||||
get_random_bytes(&bi->i_hash_seed, sizeof(bi->i_hash_seed));
|
|
||||||
SET_INODE_STR_HASH_TYPE(bi, c->sb.str_hash_type);
|
|
||||||
|
|
||||||
ret = bch_inode_create(c, &bkey_inode.k_i,
|
ret = bch_inode_create(c, &bkey_inode.k_i,
|
||||||
BLOCKDEV_INODE_MAX, 0,
|
BLOCKDEV_INODE_MAX, 0,
|
||||||
@ -239,7 +225,7 @@ static struct inode *bch_vfs_inode_create(struct cache_set *c,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
bch_inode_init(ei, inode_i_to_s_c(&bkey_inode));
|
bch_vfs_inode_init(ei, inode_i_to_s_c(&bkey_inode));
|
||||||
|
|
||||||
if (default_acl) {
|
if (default_acl) {
|
||||||
ret = bch_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
|
ret = bch_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
|
||||||
@ -270,9 +256,13 @@ static int bch_vfs_dirent_create(struct cache_set *c, struct inode *dir,
|
|||||||
u8 type, const struct qstr *name,
|
u8 type, const struct qstr *name,
|
||||||
struct inode *dst)
|
struct inode *dst)
|
||||||
{
|
{
|
||||||
|
struct bch_inode_info *dir_ei = to_bch_ei(dir);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bch_dirent_create(c, dir, type, name, dst->i_ino);
|
ret = bch_dirent_create(c, dir->i_ino, &dir_ei->str_hash,
|
||||||
|
type, name, dst->i_ino,
|
||||||
|
&dir_ei->journal_seq,
|
||||||
|
BCH_HASH_SET_MUST_CREATE);
|
||||||
if (unlikely(ret))
|
if (unlikely(ret))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -317,10 +307,13 @@ static struct dentry *bch_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct cache_set *c = dir->i_sb->s_fs_info;
|
struct cache_set *c = dir->i_sb->s_fs_info;
|
||||||
|
struct bch_inode_info *dir_ei = to_bch_ei(dir);
|
||||||
struct inode *inode = NULL;
|
struct inode *inode = NULL;
|
||||||
u64 inum;
|
u64 inum;
|
||||||
|
|
||||||
inum = bch_dirent_lookup(c, dir, &dentry->d_name);
|
inum = bch_dirent_lookup(c, dir->i_ino,
|
||||||
|
&dir_ei->str_hash,
|
||||||
|
&dentry->d_name);
|
||||||
|
|
||||||
if (inum)
|
if (inum)
|
||||||
inode = bch_vfs_inode_get(dir->i_sb, inum);
|
inode = bch_vfs_inode_get(dir->i_sb, inum);
|
||||||
@ -374,7 +367,8 @@ static int bch_unlink(struct inode *dir, struct dentry *dentry)
|
|||||||
|
|
||||||
lockdep_assert_held(&inode->i_rwsem);
|
lockdep_assert_held(&inode->i_rwsem);
|
||||||
|
|
||||||
ret = bch_dirent_delete(c, dir, &dentry->d_name);
|
ret = bch_dirent_delete(c, dir->i_ino, &dir_ei->str_hash,
|
||||||
|
&dentry->d_name, &dir_ei->journal_seq);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1016,8 +1010,8 @@ static const struct address_space_operations bch_address_space_operations = {
|
|||||||
.error_remove_page = generic_error_remove_page,
|
.error_remove_page = generic_error_remove_page,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bch_inode_init(struct bch_inode_info *ei,
|
static void bch_vfs_inode_init(struct bch_inode_info *ei,
|
||||||
struct bkey_s_c_inode bkey_inode)
|
struct bkey_s_c_inode bkey_inode)
|
||||||
{
|
{
|
||||||
struct inode *inode = &ei->vfs_inode;
|
struct inode *inode = &ei->vfs_inode;
|
||||||
const struct bch_inode *bi = bkey_inode.v;
|
const struct bch_inode *bi = bkey_inode.v;
|
||||||
@ -1044,8 +1038,7 @@ static void bch_inode_init(struct bch_inode_info *ei,
|
|||||||
inode->i_ctime = ns_to_timespec(le64_to_cpu(bi->i_ctime));
|
inode->i_ctime = ns_to_timespec(le64_to_cpu(bi->i_ctime));
|
||||||
bch_inode_flags_to_vfs(inode);
|
bch_inode_flags_to_vfs(inode);
|
||||||
|
|
||||||
ei->str_hash.seed = le64_to_cpu(bi->i_hash_seed);
|
ei->str_hash = bch_hash_info_init(bi);
|
||||||
ei->str_hash.type = INODE_STR_HASH_TYPE(bi);
|
|
||||||
|
|
||||||
inode->i_mapping->a_ops = &bch_address_space_operations;
|
inode->i_mapping->a_ops = &bch_address_space_operations;
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "keylist.h"
|
#include "keylist.h"
|
||||||
|
|
||||||
|
#include <linux/random.h>
|
||||||
|
|
||||||
ssize_t bch_inode_status(char *buf, size_t len, const struct bkey *k)
|
ssize_t bch_inode_status(char *buf, size_t len, const struct bkey *k)
|
||||||
{
|
{
|
||||||
if (k->p.offset)
|
if (k->p.offset)
|
||||||
@ -105,6 +107,28 @@ const struct bkey_ops bch_bkey_inode_ops = {
|
|||||||
.val_to_text = bch_inode_to_text,
|
.val_to_text = bch_inode_to_text,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void bch_inode_init(struct cache_set *c, struct bkey_i_inode *inode,
|
||||||
|
uid_t uid, gid_t gid, umode_t mode, dev_t rdev)
|
||||||
|
{
|
||||||
|
struct timespec ts = CURRENT_TIME;
|
||||||
|
s64 now = timespec_to_ns(&ts);
|
||||||
|
struct bch_inode *bi;
|
||||||
|
|
||||||
|
bi = &bkey_inode_init(&inode->k_i)->v;
|
||||||
|
bi->i_uid = cpu_to_le32(uid);
|
||||||
|
bi->i_gid = cpu_to_le32(gid);
|
||||||
|
|
||||||
|
bi->i_mode = cpu_to_le16(mode);
|
||||||
|
bi->i_dev = cpu_to_le32(rdev);
|
||||||
|
bi->i_atime = cpu_to_le64(now);
|
||||||
|
bi->i_mtime = cpu_to_le64(now);
|
||||||
|
bi->i_ctime = cpu_to_le64(now);
|
||||||
|
bi->i_nlink = cpu_to_le32(S_ISDIR(mode) ? 2 : 1);
|
||||||
|
|
||||||
|
get_random_bytes(&bi->i_hash_seed, sizeof(bi->i_hash_seed));
|
||||||
|
SET_INODE_STR_HASH_TYPE(bi, c->sb.str_hash_type);
|
||||||
|
}
|
||||||
|
|
||||||
int bch_inode_create(struct cache_set *c, struct bkey_i *inode,
|
int bch_inode_create(struct cache_set *c, struct bkey_i *inode,
|
||||||
u64 min, u64 max, u64 *hint)
|
u64 min, u64 max, u64 *hint)
|
||||||
{
|
{
|
||||||
@ -228,15 +252,14 @@ int bch_inode_find_by_inum(struct cache_set *c, u64 inode_nr,
|
|||||||
{
|
{
|
||||||
struct btree_iter iter;
|
struct btree_iter iter;
|
||||||
struct bkey_s_c k;
|
struct bkey_s_c k;
|
||||||
int ret = -ENOENT;
|
|
||||||
|
|
||||||
for_each_btree_key_with_holes(&iter, c, BTREE_ID_INODES,
|
for_each_btree_key_with_holes(&iter, c, BTREE_ID_INODES,
|
||||||
POS(inode_nr, 0), k) {
|
POS(inode_nr, 0), k) {
|
||||||
switch (k.k->type) {
|
switch (k.k->type) {
|
||||||
case BCH_INODE_FS:
|
case BCH_INODE_FS:
|
||||||
ret = 0;
|
|
||||||
bkey_reassemble(&inode->k_i, k);
|
bkey_reassemble(&inode->k_i, k);
|
||||||
break;
|
bch_btree_iter_unlock(&iter);
|
||||||
|
return 0;
|
||||||
default:
|
default:
|
||||||
/* hole, not found */
|
/* hole, not found */
|
||||||
break;
|
break;
|
||||||
@ -245,9 +268,8 @@ int bch_inode_find_by_inum(struct cache_set *c, u64 inode_nr,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
bch_btree_iter_unlock(&iter);
|
|
||||||
|
|
||||||
return ret;
|
return bch_btree_iter_unlock(&iter) ?: -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bch_cached_dev_inode_find_by_uuid(struct cache_set *c, uuid_le *uuid,
|
int bch_cached_dev_inode_find_by_uuid(struct cache_set *c, uuid_le *uuid,
|
||||||
|
@ -5,6 +5,8 @@ extern const struct bkey_ops bch_bkey_inode_ops;
|
|||||||
|
|
||||||
ssize_t bch_inode_status(char *, size_t, const struct bkey *);
|
ssize_t bch_inode_status(char *, size_t, const struct bkey *);
|
||||||
|
|
||||||
|
void bch_inode_init(struct cache_set *, struct bkey_i_inode *,
|
||||||
|
uid_t, gid_t, umode_t, dev_t);
|
||||||
int bch_inode_create(struct cache_set *, struct bkey_i *, u64, u64, u64 *);
|
int bch_inode_create(struct cache_set *, struct bkey_i *, u64, u64, u64 *);
|
||||||
int bch_inode_truncate(struct cache_set *, u64, u64,
|
int bch_inode_truncate(struct cache_set *, u64, u64,
|
||||||
struct extent_insert_hook *, u64 *);
|
struct extent_insert_hook *, u64 *);
|
||||||
|
@ -140,6 +140,8 @@ void bch_submit_wbio_replicas(struct bch_write_bio *wbio, struct cache_set *c,
|
|||||||
struct bch_write_bio *n;
|
struct bch_write_bio *n;
|
||||||
struct cache *ca;
|
struct cache *ca;
|
||||||
|
|
||||||
|
BUG_ON(c->opts.nochanges);
|
||||||
|
|
||||||
wbio->split = false;
|
wbio->split = false;
|
||||||
wbio->c = c;
|
wbio->c = c;
|
||||||
|
|
||||||
@ -738,7 +740,8 @@ void bch_write(struct closure *cl)
|
|||||||
!(op->flags & BCH_WRITE_CACHED),
|
!(op->flags & BCH_WRITE_CACHED),
|
||||||
op->flags & BCH_WRITE_DISCARD);
|
op->flags & BCH_WRITE_DISCARD);
|
||||||
|
|
||||||
if (!percpu_ref_tryget(&c->writes)) {
|
if (c->opts.nochanges ||
|
||||||
|
!percpu_ref_tryget(&c->writes)) {
|
||||||
__bcache_io_error(c, "read only");
|
__bcache_io_error(c, "read only");
|
||||||
op->error = -EROFS;
|
op->error = -EROFS;
|
||||||
bch_disk_reservation_put(c, &op->res);
|
bch_disk_reservation_put(c, &op->res);
|
||||||
|
@ -478,14 +478,14 @@ static int journal_validate_key(struct cache_set *c, struct jset *j,
|
|||||||
char buf[160];
|
char buf[160];
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (fsck_err_on(!k->k.u64s, c,
|
if (mustfix_fsck_err_on(!k->k.u64s, c,
|
||||||
"invalid %s in journal: k->u64s 0", type)) {
|
"invalid %s in journal: k->u64s 0", type)) {
|
||||||
entry->u64s = cpu_to_le16((u64 *) k - entry->_data);
|
entry->u64s = cpu_to_le16((u64 *) k - entry->_data);
|
||||||
journal_entry_null_range(jset_keys_next(entry), next);
|
journal_entry_null_range(jset_keys_next(entry), next);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fsck_err_on((void *) bkey_next(k) >
|
if (mustfix_fsck_err_on((void *) bkey_next(k) >
|
||||||
(void *) jset_keys_next(entry), c,
|
(void *) jset_keys_next(entry), c,
|
||||||
"invalid %s in journal: extends past end of journal entry",
|
"invalid %s in journal: extends past end of journal entry",
|
||||||
type)) {
|
type)) {
|
||||||
@ -494,7 +494,7 @@ static int journal_validate_key(struct cache_set *c, struct jset *j,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fsck_err_on(k->k.format != KEY_FORMAT_CURRENT, c,
|
if (mustfix_fsck_err_on(k->k.format != KEY_FORMAT_CURRENT, c,
|
||||||
"invalid %s in journal: bad format %u",
|
"invalid %s in journal: bad format %u",
|
||||||
type, k->k.format)) {
|
type, k->k.format)) {
|
||||||
le16_add_cpu(&entry->u64s, -k->k.u64s);
|
le16_add_cpu(&entry->u64s, -k->k.u64s);
|
||||||
@ -510,7 +510,7 @@ static int journal_validate_key(struct cache_set *c, struct jset *j,
|
|||||||
if (invalid) {
|
if (invalid) {
|
||||||
bch_bkey_val_to_text(c, key_type, buf, sizeof(buf),
|
bch_bkey_val_to_text(c, key_type, buf, sizeof(buf),
|
||||||
bkey_i_to_s_c(k));
|
bkey_i_to_s_c(k));
|
||||||
fsck_err(c, "invalid %s in journal: %s", type, buf);
|
mustfix_fsck_err(c, "invalid %s in journal: %s", type, buf);
|
||||||
|
|
||||||
le16_add_cpu(&entry->u64s, -k->k.u64s);
|
le16_add_cpu(&entry->u64s, -k->k.u64s);
|
||||||
memmove(k, bkey_next(k), next - (void *) bkey_next(k));
|
memmove(k, bkey_next(k), next - (void *) bkey_next(k));
|
||||||
@ -543,7 +543,7 @@ static int journal_entry_validate(struct cache_set *c, struct jset *j, u64 secto
|
|||||||
return BCH_FSCK_UNKNOWN_VERSION;
|
return BCH_FSCK_UNKNOWN_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fsck_err_on(bytes > bucket_sectors_left << 9 ||
|
if (mustfix_fsck_err_on(bytes > bucket_sectors_left << 9 ||
|
||||||
bytes > c->journal.entry_size_max, c,
|
bytes > c->journal.entry_size_max, c,
|
||||||
"journal entry too big (%zu bytes), sector %lluu",
|
"journal entry too big (%zu bytes), sector %lluu",
|
||||||
bytes, sector)) {
|
bytes, sector)) {
|
||||||
@ -556,7 +556,7 @@ static int journal_entry_validate(struct cache_set *c, struct jset *j, u64 secto
|
|||||||
|
|
||||||
got = le64_to_cpu(j->csum);
|
got = le64_to_cpu(j->csum);
|
||||||
expect = __csum_set(j, le32_to_cpu(j->u64s), JSET_CSUM_TYPE(j));
|
expect = __csum_set(j, le32_to_cpu(j->u64s), JSET_CSUM_TYPE(j));
|
||||||
if (fsck_err_on(got != expect, c,
|
if (mustfix_fsck_err_on(got != expect, c,
|
||||||
"journal checksum bad (got %llu expect %llu), sector %lluu",
|
"journal checksum bad (got %llu expect %llu), sector %lluu",
|
||||||
got, expect, sector)) {
|
got, expect, sector)) {
|
||||||
/* XXX: retry IO, when we start retrying checksum errors */
|
/* XXX: retry IO, when we start retrying checksum errors */
|
||||||
@ -564,14 +564,14 @@ static int journal_entry_validate(struct cache_set *c, struct jset *j, u64 secto
|
|||||||
return JOURNAL_ENTRY_BAD;
|
return JOURNAL_ENTRY_BAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fsck_err_on(le64_to_cpu(j->last_seq) > le64_to_cpu(j->seq), c,
|
if (mustfix_fsck_err_on(le64_to_cpu(j->last_seq) > le64_to_cpu(j->seq),
|
||||||
"invalid journal entry: last_seq > seq"))
|
c, "invalid journal entry: last_seq > seq"))
|
||||||
j->last_seq = j->seq;
|
j->last_seq = j->seq;
|
||||||
|
|
||||||
for_each_jset_entry(entry, j) {
|
for_each_jset_entry(entry, j) {
|
||||||
struct bkey_i *k;
|
struct bkey_i *k;
|
||||||
|
|
||||||
if (fsck_err_on(jset_keys_next(entry) >
|
if (mustfix_fsck_err_on(jset_keys_next(entry) >
|
||||||
bkey_idx(j, le32_to_cpu(j->u64s)), c,
|
bkey_idx(j, le32_to_cpu(j->u64s)), c,
|
||||||
"journal entry extents past end of jset")) {
|
"journal entry extents past end of jset")) {
|
||||||
j->u64s = cpu_to_le64((u64 *) entry - j->_data);
|
j->u64s = cpu_to_le64((u64 *) entry - j->_data);
|
||||||
@ -595,7 +595,7 @@ static int journal_entry_validate(struct cache_set *c, struct jset *j, u64 secto
|
|||||||
case JOURNAL_ENTRY_BTREE_ROOT:
|
case JOURNAL_ENTRY_BTREE_ROOT:
|
||||||
k = entry->start;
|
k = entry->start;
|
||||||
|
|
||||||
if (fsck_err_on(!entry->u64s ||
|
if (mustfix_fsck_err_on(!entry->u64s ||
|
||||||
le16_to_cpu(entry->u64s) != k->k.u64s, c,
|
le16_to_cpu(entry->u64s) != k->k.u64s, c,
|
||||||
"invalid btree root journal entry: wrong number of keys")) {
|
"invalid btree root journal entry: wrong number of keys")) {
|
||||||
journal_entry_null_range(entry,
|
journal_entry_null_range(entry,
|
||||||
@ -613,7 +613,7 @@ static int journal_entry_validate(struct cache_set *c, struct jset *j, u64 secto
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case JOURNAL_ENTRY_JOURNAL_SEQ_BLACKLISTED:
|
case JOURNAL_ENTRY_JOURNAL_SEQ_BLACKLISTED:
|
||||||
if (fsck_err_on(le16_to_cpu(entry->u64s) != 1, c,
|
if (mustfix_fsck_err_on(le16_to_cpu(entry->u64s) != 1, c,
|
||||||
"invalid journal seq blacklist entry: bad size")) {
|
"invalid journal seq blacklist entry: bad size")) {
|
||||||
journal_entry_null_range(entry,
|
journal_entry_null_range(entry,
|
||||||
jset_keys_next(entry));
|
jset_keys_next(entry));
|
||||||
@ -621,7 +621,7 @@ static int journal_entry_validate(struct cache_set *c, struct jset *j, u64 secto
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fsck_err(c, "invalid journal entry type %llu",
|
mustfix_fsck_err(c, "invalid journal entry type %llu",
|
||||||
JOURNAL_ENTRY_TYPE(entry));
|
JOURNAL_ENTRY_TYPE(entry));
|
||||||
journal_entry_null_range(entry, jset_keys_next(entry));
|
journal_entry_null_range(entry, jset_keys_next(entry));
|
||||||
break;
|
break;
|
||||||
@ -2065,6 +2065,13 @@ static void journal_write(struct closure *cl)
|
|||||||
|
|
||||||
bch_check_mark_super(c, &j->key, true);
|
bch_check_mark_super(c, &j->key, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX: we really should just disable the entire journal in nochanges
|
||||||
|
* mode
|
||||||
|
*/
|
||||||
|
if (c->opts.nochanges)
|
||||||
|
goto no_io;
|
||||||
|
|
||||||
extent_for_each_ptr(bkey_i_to_s_extent(&j->key), ptr) {
|
extent_for_each_ptr(bkey_i_to_s_extent(&j->key), ptr) {
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
ca = PTR_CACHE(c, ptr);
|
ca = PTR_CACHE(c, ptr);
|
||||||
@ -2094,8 +2101,6 @@ static void journal_write(struct closure *cl)
|
|||||||
trace_bcache_journal_write(bio);
|
trace_bcache_journal_write(bio);
|
||||||
closure_bio_submit_punt(bio, cl, c);
|
closure_bio_submit_punt(bio, cl, c);
|
||||||
|
|
||||||
ptr->offset += sectors;
|
|
||||||
|
|
||||||
ca->journal.bucket_seq[ca->journal.cur_idx] = le64_to_cpu(w->data->seq);
|
ca->journal.bucket_seq[ca->journal.cur_idx] = le64_to_cpu(w->data->seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2114,6 +2119,10 @@ static void journal_write(struct closure *cl)
|
|||||||
closure_bio_submit_punt(bio, cl, c);
|
closure_bio_submit_punt(bio, cl, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
no_io:
|
||||||
|
extent_for_each_ptr(bkey_i_to_s_extent(&j->key), ptr)
|
||||||
|
ptr->offset += sectors;
|
||||||
|
|
||||||
closure_return_with_destructor(cl, journal_write_done);
|
closure_return_with_destructor(cl, journal_write_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,14 +26,11 @@ static const struct bch_extent_ptr *moving_pred(struct cache *ca,
|
|||||||
{
|
{
|
||||||
const struct bch_extent_ptr *ptr;
|
const struct bch_extent_ptr *ptr;
|
||||||
|
|
||||||
if (bkey_extent_is_data(k.k)) {
|
if (bkey_extent_is_data(k.k) &&
|
||||||
struct bkey_s_c_extent e = bkey_s_c_to_extent(k);
|
(ptr = bch_extent_has_device(bkey_s_c_to_extent(k),
|
||||||
|
ca->sb.nr_this_dev)) &&
|
||||||
extent_for_each_ptr(e, ptr)
|
PTR_BUCKET(ca, ptr)->mark.copygc)
|
||||||
if ((ca->sb.nr_this_dev == ptr->dev) &&
|
return ptr;
|
||||||
PTR_BUCKET(ca, ptr)->mark.copygc)
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -274,6 +271,9 @@ int bch_moving_gc_thread_start(struct cache *ca)
|
|||||||
/* The moving gc read thread must be stopped */
|
/* The moving gc read thread must be stopped */
|
||||||
BUG_ON(ca->moving_gc_read != NULL);
|
BUG_ON(ca->moving_gc_read != NULL);
|
||||||
|
|
||||||
|
if (ca->set->opts.nochanges)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (cache_set_init_fault("moving_gc_start"))
|
if (cache_set_init_fault("moving_gc_start"))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -4,16 +4,6 @@
|
|||||||
#include "opts.h"
|
#include "opts.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
const char * const bch_bool_opt[] = {
|
|
||||||
"0",
|
|
||||||
"1",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * const bch_uint_opt[] = {
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * const bch_error_actions[] = {
|
const char * const bch_error_actions[] = {
|
||||||
"continue",
|
"continue",
|
||||||
"remount-ro",
|
"remount-ro",
|
||||||
@ -43,6 +33,42 @@ const char * const bch_str_hash_types[] = {
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char * const bch_cache_replacement_policies[] = {
|
||||||
|
"lru",
|
||||||
|
"fifo",
|
||||||
|
"random",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Default is -1; we skip past it for struct cached_dev's cache mode */
|
||||||
|
const char * const bch_cache_modes[] = {
|
||||||
|
"default",
|
||||||
|
"writethrough",
|
||||||
|
"writeback",
|
||||||
|
"writearound",
|
||||||
|
"none",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
const char * const bch_cache_state[] = {
|
||||||
|
"active",
|
||||||
|
"readonly",
|
||||||
|
"failed",
|
||||||
|
"spare",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const char * const bch_bool_opt[] = {
|
||||||
|
"0",
|
||||||
|
"1",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
const char * const bch_uint_opt[] = {
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
enum bch_opts {
|
enum bch_opts {
|
||||||
#define CACHE_SET_OPT(_name, _choices, _min, _max, _sb_opt, _perm) \
|
#define CACHE_SET_OPT(_name, _choices, _min, _max, _sb_opt, _perm) \
|
||||||
Opt_##_name,
|
Opt_##_name,
|
||||||
|
@ -6,6 +6,14 @@
|
|||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
|
|
||||||
|
extern const char * const bch_error_actions[];
|
||||||
|
extern const char * const bch_csum_types[];
|
||||||
|
extern const char * const bch_compression_types[];
|
||||||
|
extern const char * const bch_str_hash_types[];
|
||||||
|
extern const char * const bch_cache_replacement_policies[];
|
||||||
|
extern const char * const bch_cache_modes[];
|
||||||
|
extern const char * const bch_cache_state[];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mount options; we also store defaults in the superblock.
|
* Mount options; we also store defaults in the superblock.
|
||||||
*
|
*
|
||||||
@ -20,10 +28,6 @@
|
|||||||
|
|
||||||
extern const char * const bch_bool_opt[];
|
extern const char * const bch_bool_opt[];
|
||||||
extern const char * const bch_uint_opt[];
|
extern const char * const bch_uint_opt[];
|
||||||
extern const char * const bch_error_actions[];
|
|
||||||
extern const char * const bch_csum_types[];
|
|
||||||
extern const char * const bch_compression_types[];
|
|
||||||
extern const char * const bch_str_hash_types[];
|
|
||||||
|
|
||||||
/* dummy option, for options that aren't stored in the superblock */
|
/* dummy option, for options that aren't stored in the superblock */
|
||||||
LE64_BITMASK(NO_SB_OPT, struct cache_sb, flags, 0, 0);
|
LE64_BITMASK(NO_SB_OPT, struct cache_sb, flags, 0, 0);
|
||||||
@ -44,6 +48,15 @@ LE64_BITMASK(NO_SB_OPT, struct cache_sb, flags, 0, 0);
|
|||||||
CACHE_SET_OPT(fix_errors, \
|
CACHE_SET_OPT(fix_errors, \
|
||||||
bch_bool_opt, 0, 2, \
|
bch_bool_opt, 0, 2, \
|
||||||
NO_SB_OPT, true) \
|
NO_SB_OPT, true) \
|
||||||
|
CACHE_SET_OPT(nochanges, \
|
||||||
|
bch_bool_opt, 0, 2, \
|
||||||
|
NO_SB_OPT, 0) \
|
||||||
|
CACHE_SET_OPT(noreplay, \
|
||||||
|
bch_bool_opt, 0, 2, \
|
||||||
|
NO_SB_OPT, 0) \
|
||||||
|
CACHE_SET_OPT(norecovery, \
|
||||||
|
bch_bool_opt, 0, 2, \
|
||||||
|
NO_SB_OPT, 0) \
|
||||||
CACHE_SET_SB_OPTS()
|
CACHE_SET_SB_OPTS()
|
||||||
|
|
||||||
#define CACHE_SET_OPTS() \
|
#define CACHE_SET_OPTS() \
|
||||||
|
@ -79,6 +79,14 @@ struct bch_hash_info {
|
|||||||
u8 type;
|
u8 type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline struct bch_hash_info bch_hash_info_init(const struct bch_inode *bi)
|
||||||
|
{
|
||||||
|
return (struct bch_hash_info) {
|
||||||
|
.seed = le64_to_cpu(bi->i_hash_seed),
|
||||||
|
.type = INODE_STR_HASH_TYPE(bi),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
struct bch_hash_desc {
|
struct bch_hash_desc {
|
||||||
enum btree_id btree_id;
|
enum btree_id btree_id;
|
||||||
u8 key_type;
|
u8 key_type;
|
||||||
|
@ -99,14 +99,17 @@ static bool bch_is_open(struct block_device *bdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const char *bch_blkdev_open(const char *path, void *holder,
|
static const char *bch_blkdev_open(const char *path, void *holder,
|
||||||
|
struct cache_set_opts opts,
|
||||||
struct block_device **ret)
|
struct block_device **ret)
|
||||||
{
|
{
|
||||||
struct block_device *bdev;
|
struct block_device *bdev;
|
||||||
|
fmode_t mode = opts.nochanges > 0
|
||||||
|
? FMODE_READ
|
||||||
|
: FMODE_READ|FMODE_WRITE|FMODE_EXCL;
|
||||||
const char *err;
|
const char *err;
|
||||||
|
|
||||||
*ret = NULL;
|
*ret = NULL;
|
||||||
bdev = blkdev_get_by_path(path, FMODE_READ|FMODE_WRITE|FMODE_EXCL,
|
bdev = blkdev_get_by_path(path, mode, holder);
|
||||||
holder);
|
|
||||||
|
|
||||||
if (bdev == ERR_PTR(-EBUSY)) {
|
if (bdev == ERR_PTR(-EBUSY)) {
|
||||||
bdev = lookup_bdev(path);
|
bdev = lookup_bdev(path);
|
||||||
@ -369,6 +372,7 @@ int bch_super_realloc(struct bcache_superblock *sb, unsigned u64s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const char *read_super(struct bcache_superblock *sb,
|
static const char *read_super(struct bcache_superblock *sb,
|
||||||
|
struct cache_set_opts opts,
|
||||||
const char *path)
|
const char *path)
|
||||||
{
|
{
|
||||||
const char *err;
|
const char *err;
|
||||||
@ -378,7 +382,7 @@ static const char *read_super(struct bcache_superblock *sb,
|
|||||||
|
|
||||||
memset(sb, 0, sizeof(*sb));
|
memset(sb, 0, sizeof(*sb));
|
||||||
|
|
||||||
err = bch_blkdev_open(path, &sb, &sb->bdev);
|
err = bch_blkdev_open(path, &sb, opts, &sb->bdev);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
retry:
|
retry:
|
||||||
@ -614,6 +618,9 @@ static void __bcache_write_super(struct cache_set *c)
|
|||||||
|
|
||||||
closure_init(cl, &c->cl);
|
closure_init(cl, &c->cl);
|
||||||
|
|
||||||
|
if (c->opts.nochanges)
|
||||||
|
goto no_io;
|
||||||
|
|
||||||
le64_add_cpu(&c->disk_sb.seq, 1);
|
le64_add_cpu(&c->disk_sb.seq, 1);
|
||||||
|
|
||||||
for_each_cache(ca, c, i) {
|
for_each_cache(ca, c, i) {
|
||||||
@ -636,7 +643,7 @@ static void __bcache_write_super(struct cache_set *c)
|
|||||||
percpu_ref_get(&ca->ref);
|
percpu_ref_get(&ca->ref);
|
||||||
__write_super(c, &ca->disk_sb);
|
__write_super(c, &ca->disk_sb);
|
||||||
}
|
}
|
||||||
|
no_io:
|
||||||
closure_return_with_destructor(cl, bcache_write_super_unlock);
|
closure_return_with_destructor(cl, bcache_write_super_unlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1147,6 +1154,9 @@ static struct cache_set *bch_cache_set_alloc(struct cache_sb *sb,
|
|||||||
c->opts = cache_superblock_opts(sb);
|
c->opts = cache_superblock_opts(sb);
|
||||||
cache_set_opts_apply(&c->opts, opts);
|
cache_set_opts_apply(&c->opts, opts);
|
||||||
|
|
||||||
|
c->opts.nochanges |= c->opts.noreplay;
|
||||||
|
c->opts.read_only |= c->opts.nochanges;
|
||||||
|
|
||||||
c->block_bits = ilog2(c->sb.block_size);
|
c->block_bits = ilog2(c->sb.block_size);
|
||||||
|
|
||||||
if (cache_set_init_fault("cache_set_alloc"))
|
if (cache_set_init_fault("cache_set_alloc"))
|
||||||
@ -1339,6 +1349,9 @@ static const char *run_cache_set(struct cache_set *c)
|
|||||||
if (bch_initial_gc(c, &journal))
|
if (bch_initial_gc(c, &journal))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
if (c->opts.noreplay)
|
||||||
|
goto recovery_done;
|
||||||
|
|
||||||
bch_verbose(c, "mark and sweep done");
|
bch_verbose(c, "mark and sweep done");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1365,6 +1378,9 @@ static const char *run_cache_set(struct cache_set *c)
|
|||||||
|
|
||||||
bch_verbose(c, "journal replay done");
|
bch_verbose(c, "journal replay done");
|
||||||
|
|
||||||
|
if (c->opts.norecovery)
|
||||||
|
goto recovery_done;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write a new journal entry _before_ we start journalling new
|
* Write a new journal entry _before_ we start journalling new
|
||||||
* data - otherwise, we could end up with btree node bsets with
|
* data - otherwise, we could end up with btree node bsets with
|
||||||
@ -1376,21 +1392,12 @@ static const char *run_cache_set(struct cache_set *c)
|
|||||||
if (bch_journal_meta(&c->journal))
|
if (bch_journal_meta(&c->journal))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
bch_verbose(c, "starting fs gc:");
|
bch_verbose(c, "starting fsck:");
|
||||||
err = "error in fs gc";
|
err = "error in fsck";
|
||||||
ret = bch_gc_inode_nlinks(c);
|
ret = bch_fsck(c, !c->opts.nofsck);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
bch_verbose(c, "fs gc done");
|
bch_verbose(c, "fsck done");
|
||||||
|
|
||||||
if (!c->opts.nofsck) {
|
|
||||||
bch_verbose(c, "starting fsck:");
|
|
||||||
err = "error in fsck";
|
|
||||||
ret = bch_fsck(c);
|
|
||||||
if (ret)
|
|
||||||
goto err;
|
|
||||||
bch_verbose(c, "fsck done");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
struct bkey_i_inode inode;
|
struct bkey_i_inode inode;
|
||||||
struct closure cl;
|
struct closure cl;
|
||||||
@ -1433,12 +1440,9 @@ static const char *run_cache_set(struct cache_set *c)
|
|||||||
/* Wait for new btree roots to be written: */
|
/* Wait for new btree roots to be written: */
|
||||||
closure_sync(&cl);
|
closure_sync(&cl);
|
||||||
|
|
||||||
bkey_inode_init(&inode.k_i);
|
bch_inode_init(c, &inode, 0, 0,
|
||||||
|
S_IFDIR|S_IRWXU|S_IRUGO|S_IXUGO, 0);
|
||||||
inode.k.p.inode = BCACHE_ROOT_INO;
|
inode.k.p.inode = BCACHE_ROOT_INO;
|
||||||
inode.v.i_mode = cpu_to_le16(S_IFDIR|S_IRWXU|S_IRUGO|S_IXUGO);
|
|
||||||
inode.v.i_nlink = cpu_to_le32(2);
|
|
||||||
get_random_bytes(&inode.v.i_hash_seed, sizeof(inode.v.i_hash_seed));
|
|
||||||
SET_INODE_STR_HASH_TYPE(&inode.v, c->sb.str_hash_type);
|
|
||||||
|
|
||||||
err = "error creating root directory";
|
err = "error creating root directory";
|
||||||
if (bch_btree_insert(c, BTREE_ID_INODES, &inode.k_i,
|
if (bch_btree_insert(c, BTREE_ID_INODES, &inode.k_i,
|
||||||
@ -1449,7 +1453,7 @@ static const char *run_cache_set(struct cache_set *c)
|
|||||||
if (bch_journal_meta(&c->journal))
|
if (bch_journal_meta(&c->journal))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
recovery_done:
|
||||||
if (c->opts.read_only) {
|
if (c->opts.read_only) {
|
||||||
bch_cache_set_read_only_sync(c);
|
bch_cache_set_read_only_sync(c);
|
||||||
} else {
|
} else {
|
||||||
@ -1485,12 +1489,12 @@ static const char *run_cache_set(struct cache_set *c)
|
|||||||
set_bit(CACHE_SET_RUNNING, &c->flags);
|
set_bit(CACHE_SET_RUNNING, &c->flags);
|
||||||
bch_attach_backing_devs(c);
|
bch_attach_backing_devs(c);
|
||||||
|
|
||||||
closure_put(&c->caching);
|
|
||||||
|
|
||||||
bch_notify_cache_set_read_write(c);
|
bch_notify_cache_set_read_write(c);
|
||||||
|
err = NULL;
|
||||||
BUG_ON(!list_empty(&journal));
|
out:
|
||||||
return NULL;
|
bch_journal_entries_free(&journal);
|
||||||
|
closure_put(&c->caching);
|
||||||
|
return err;
|
||||||
err:
|
err:
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case BCH_FSCK_ERRORS_NOT_FIXED:
|
case BCH_FSCK_ERRORS_NOT_FIXED:
|
||||||
@ -1519,12 +1523,8 @@ err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
BUG_ON(!err);
|
BUG_ON(!err);
|
||||||
|
|
||||||
bch_journal_entries_free(&journal);
|
|
||||||
set_bit(CACHE_SET_ERROR, &c->flags);
|
set_bit(CACHE_SET_ERROR, &c->flags);
|
||||||
bch_cache_set_unregister(c);
|
goto out;
|
||||||
closure_put(&c->caching);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *can_add_cache(struct cache_sb *sb,
|
static const char *can_add_cache(struct cache_sb *sb,
|
||||||
@ -2056,8 +2056,9 @@ static const char *register_cache(struct bcache_superblock *sb,
|
|||||||
struct cache_set_opts opts)
|
struct cache_set_opts opts)
|
||||||
{
|
{
|
||||||
char name[BDEVNAME_SIZE];
|
char name[BDEVNAME_SIZE];
|
||||||
const char *err = "cannot allocate memory";
|
const char *err;
|
||||||
struct cache_set *c;
|
struct cache_set *c;
|
||||||
|
bool allocated_cache_set = false;
|
||||||
|
|
||||||
err = validate_cache_super(sb);
|
err = validate_cache_super(sb);
|
||||||
if (err)
|
if (err)
|
||||||
@ -2067,41 +2068,36 @@ static const char *register_cache(struct bcache_superblock *sb,
|
|||||||
|
|
||||||
c = cache_set_lookup(sb->sb->set_uuid);
|
c = cache_set_lookup(sb->sb->set_uuid);
|
||||||
if (c) {
|
if (c) {
|
||||||
if ((err = (can_attach_cache(sb->sb, c) ?:
|
err = can_attach_cache(sb->sb, c);
|
||||||
cache_alloc(sb, c, NULL))))
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
} else {
|
||||||
|
c = bch_cache_set_alloc(sb->sb, opts);
|
||||||
|
if (!c)
|
||||||
|
return "cannot allocate memory";
|
||||||
|
|
||||||
if (cache_set_nr_online_devices(c) == cache_set_nr_devices(c)) {
|
allocated_cache_set = true;
|
||||||
err = run_cache_set(c);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c = bch_cache_set_alloc(sb->sb, opts);
|
|
||||||
if (!c)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
err = cache_alloc(sb, c, NULL);
|
err = cache_alloc(sb, c, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_stop;
|
goto err;
|
||||||
|
|
||||||
if (cache_set_nr_online_devices(c) == cache_set_nr_devices(c)) {
|
if (cache_set_nr_online_devices(c) == cache_set_nr_devices(c)) {
|
||||||
err = run_cache_set(c);
|
err = run_cache_set(c);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_stop;
|
goto err;
|
||||||
|
} else {
|
||||||
|
err = "error creating kobject";
|
||||||
|
if (bch_cache_set_online(c))
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = "error creating kobject";
|
|
||||||
if (bch_cache_set_online(c))
|
|
||||||
goto err_stop;
|
|
||||||
out:
|
|
||||||
|
|
||||||
bch_info(c, "started");
|
bch_info(c, "started");
|
||||||
return NULL;
|
return NULL;
|
||||||
err_stop:
|
err:
|
||||||
bch_cache_set_stop(c);
|
if (allocated_cache_set)
|
||||||
|
bch_cache_set_stop(c);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2117,7 +2113,7 @@ int bch_cache_set_add_cache(struct cache_set *c, const char *path)
|
|||||||
|
|
||||||
mutex_lock(&bch_register_lock);
|
mutex_lock(&bch_register_lock);
|
||||||
|
|
||||||
err = read_super(&sb, path);
|
err = read_super(&sb, c->opts, path);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
|
|
||||||
@ -2261,7 +2257,7 @@ const char *bch_register_cache_set(char * const *devices, unsigned nr_devices,
|
|||||||
mutex_lock(&bch_register_lock);
|
mutex_lock(&bch_register_lock);
|
||||||
|
|
||||||
for (i = 0; i < nr_devices; i++) {
|
for (i = 0; i < nr_devices; i++) {
|
||||||
err = read_super(&sb[i], devices[i]);
|
err = read_super(&sb[i], opts, devices[i]);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
|
|
||||||
@ -2312,6 +2308,8 @@ const char *bch_register_cache_set(char * const *devices, unsigned nr_devices,
|
|||||||
out:
|
out:
|
||||||
kfree(sb);
|
kfree(sb);
|
||||||
module_put(THIS_MODULE);
|
module_put(THIS_MODULE);
|
||||||
|
if (err)
|
||||||
|
c = NULL;
|
||||||
return err;
|
return err;
|
||||||
err_unlock:
|
err_unlock:
|
||||||
if (c)
|
if (c)
|
||||||
@ -2326,18 +2324,19 @@ err:
|
|||||||
const char *bch_register_one(const char *path)
|
const char *bch_register_one(const char *path)
|
||||||
{
|
{
|
||||||
struct bcache_superblock sb;
|
struct bcache_superblock sb;
|
||||||
|
struct cache_set_opts opts = cache_set_opts_empty();
|
||||||
const char *err;
|
const char *err;
|
||||||
|
|
||||||
mutex_lock(&bch_register_lock);
|
mutex_lock(&bch_register_lock);
|
||||||
|
|
||||||
err = read_super(&sb, path);
|
err = read_super(&sb, opts, path);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (__SB_IS_BDEV(le64_to_cpu(sb.sb->version)))
|
if (__SB_IS_BDEV(le64_to_cpu(sb.sb->version)))
|
||||||
err = bch_backing_dev_register(&sb);
|
err = bch_backing_dev_register(&sb);
|
||||||
else
|
else
|
||||||
err = register_cache(&sb, cache_set_opts_empty());
|
err = register_cache(&sb, opts);
|
||||||
|
|
||||||
free_super(&sb);
|
free_super(&sb);
|
||||||
err:
|
err:
|
||||||
|
@ -24,31 +24,6 @@
|
|||||||
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
||||||
#include <linux/sort.h>
|
#include <linux/sort.h>
|
||||||
|
|
||||||
static const char * const cache_replacement_policies[] = {
|
|
||||||
"lru",
|
|
||||||
"fifo",
|
|
||||||
"random",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Default is -1; we skip past it for struct cached_dev's cache mode */
|
|
||||||
static const char * const bch_cache_modes[] = {
|
|
||||||
"default",
|
|
||||||
"writethrough",
|
|
||||||
"writeback",
|
|
||||||
"writearound",
|
|
||||||
"none",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char * const bch_cache_state[] = {
|
|
||||||
"active",
|
|
||||||
"readonly",
|
|
||||||
"failed",
|
|
||||||
"spare",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
write_attribute(attach);
|
write_attribute(attach);
|
||||||
write_attribute(detach);
|
write_attribute(detach);
|
||||||
write_attribute(unregister);
|
write_attribute(unregister);
|
||||||
@ -1237,7 +1212,7 @@ SHOW(bch_cache)
|
|||||||
|
|
||||||
if (attr == &sysfs_cache_replacement_policy)
|
if (attr == &sysfs_cache_replacement_policy)
|
||||||
return bch_snprint_string_list(buf, PAGE_SIZE,
|
return bch_snprint_string_list(buf, PAGE_SIZE,
|
||||||
cache_replacement_policies,
|
bch_cache_replacement_policies,
|
||||||
ca->mi.replacement);
|
ca->mi.replacement);
|
||||||
|
|
||||||
sysfs_print(tier, ca->mi.tier);
|
sysfs_print(tier, ca->mi.tier);
|
||||||
@ -1281,7 +1256,7 @@ STORE(__bch_cache)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (attr == &sysfs_cache_replacement_policy) {
|
if (attr == &sysfs_cache_replacement_policy) {
|
||||||
ssize_t v = bch_read_string_list(buf, cache_replacement_policies);
|
ssize_t v = bch_read_string_list(buf, bch_cache_replacement_policies);
|
||||||
|
|
||||||
if (v < 0)
|
if (v < 0)
|
||||||
return v;
|
return v;
|
||||||
|
@ -224,6 +224,9 @@ int bch_tiering_read_start(struct cache_set *c)
|
|||||||
{
|
{
|
||||||
struct task_struct *t;
|
struct task_struct *t;
|
||||||
|
|
||||||
|
if (c->opts.nochanges)
|
||||||
|
return 0;
|
||||||
|
|
||||||
t = kthread_create(bch_tiering_thread, c, "bch_tier_read");
|
t = kthread_create(bch_tiering_thread, c, "bch_tier_read");
|
||||||
if (IS_ERR(t))
|
if (IS_ERR(t))
|
||||||
return PTR_ERR(t);
|
return PTR_ERR(t);
|
||||||
|
102
tools-util.c
102
tools-util.c
@ -18,11 +18,13 @@
|
|||||||
|
|
||||||
#include "ccan/crc/crc.h"
|
#include "ccan/crc/crc.h"
|
||||||
|
|
||||||
|
#include "linux/bcache-ioctl.h"
|
||||||
#include "tools-util.h"
|
#include "tools-util.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
/* Integer stuff: */
|
/* Integer stuff: */
|
||||||
|
|
||||||
struct units_buf pr_units(u64 v, enum units units)
|
struct units_buf __pr_units(u64 v, enum units units)
|
||||||
{
|
{
|
||||||
struct units_buf ret;
|
struct units_buf ret;
|
||||||
|
|
||||||
@ -53,65 +55,6 @@ struct units_buf pr_units(u64 v, enum units units)
|
|||||||
|
|
||||||
/* Argument parsing stuff: */
|
/* Argument parsing stuff: */
|
||||||
|
|
||||||
long strtoul_or_die(const char *p, size_t max, const char *msg)
|
|
||||||
{
|
|
||||||
errno = 0;
|
|
||||||
long v = strtol(p, NULL, 10);
|
|
||||||
if (errno || v < 0 || v >= max)
|
|
||||||
die("Invalid %s %zi", msg, v);
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 hatoi(const char *s)
|
|
||||||
{
|
|
||||||
char *e;
|
|
||||||
long long i = strtoll(s, &e, 10);
|
|
||||||
switch (*e) {
|
|
||||||
case 't':
|
|
||||||
case 'T':
|
|
||||||
i *= 1024;
|
|
||||||
case 'g':
|
|
||||||
case 'G':
|
|
||||||
i *= 1024;
|
|
||||||
case 'm':
|
|
||||||
case 'M':
|
|
||||||
i *= 1024;
|
|
||||||
case 'k':
|
|
||||||
case 'K':
|
|
||||||
i *= 1024;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned hatoi_validate(const char *s, const char *msg)
|
|
||||||
{
|
|
||||||
u64 v = hatoi(s);
|
|
||||||
|
|
||||||
if (v & (v - 1))
|
|
||||||
die("%s must be a power of two", msg);
|
|
||||||
|
|
||||||
v /= 512;
|
|
||||||
|
|
||||||
if (v > USHRT_MAX)
|
|
||||||
die("%s too large\n", msg);
|
|
||||||
|
|
||||||
if (!v)
|
|
||||||
die("%s too small\n", msg);
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned nr_args(char * const *args)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
for (i = 0; args[i]; i++)
|
|
||||||
;
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* File parsing (i.e. sysfs) */
|
/* File parsing (i.e. sysfs) */
|
||||||
|
|
||||||
char *read_file_str(int dirfd, const char *path)
|
char *read_file_str(int dirfd, const char *path)
|
||||||
@ -151,48 +94,16 @@ u64 read_file_u64(int dirfd, const char *path)
|
|||||||
|
|
||||||
/* String list options: */
|
/* String list options: */
|
||||||
|
|
||||||
ssize_t read_string_list(const char *buf, const char * const list[])
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
char *s, *d = strdup(buf);
|
|
||||||
if (!d)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
s = strim(d);
|
|
||||||
|
|
||||||
for (i = 0; list[i]; i++)
|
|
||||||
if (!strcmp(list[i], s))
|
|
||||||
break;
|
|
||||||
|
|
||||||
free(d);
|
|
||||||
|
|
||||||
if (!list[i])
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t read_string_list_or_die(const char *opt, const char * const list[],
|
ssize_t read_string_list_or_die(const char *opt, const char * const list[],
|
||||||
const char *msg)
|
const char *msg)
|
||||||
{
|
{
|
||||||
ssize_t v = read_string_list(opt, list);
|
ssize_t v = bch_read_string_list(opt, list);
|
||||||
if (v < 0)
|
if (v < 0)
|
||||||
die("Bad %s %s", msg, opt);
|
die("Bad %s %s", msg, opt);
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_string_list(const char * const list[], size_t selected)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = 0; list[i]; i++) {
|
|
||||||
if (i)
|
|
||||||
putchar(' ');
|
|
||||||
printf(i == selected ? "[%s] ": "%s", list[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns size of file or block device, in units of 512 byte sectors: */
|
/* Returns size of file or block device, in units of 512 byte sectors: */
|
||||||
u64 get_size(const char *path, int fd)
|
u64 get_size(const char *path, int fd)
|
||||||
{
|
{
|
||||||
@ -296,14 +207,15 @@ struct bcache_handle bcache_fs_open(const char *path)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ask_proceed(void)
|
bool ask_yn(void)
|
||||||
{
|
{
|
||||||
const char *short_yes = "yY";
|
const char *short_yes = "yY";
|
||||||
char *buf = NULL;
|
char *buf = NULL;
|
||||||
size_t buflen = 0;
|
size_t buflen = 0;
|
||||||
bool ret;
|
bool ret;
|
||||||
|
|
||||||
fputs("Proceed anyway? (y,n) ", stdout);
|
fputs(" (y,n) ", stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
if (getline(&buf, &buflen, stdin) < 0)
|
if (getline(&buf, &buflen, stdin) < 0)
|
||||||
die("error reading from standard input");
|
die("error reading from standard input");
|
||||||
|
54
tools-util.h
54
tools-util.h
@ -1,10 +1,12 @@
|
|||||||
#ifndef _TOOLS_UTIL_H
|
#ifndef _TOOLS_UTIL_H
|
||||||
#define _TOOLS_UTIL_H
|
#define _TOOLS_UTIL_H
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <linux/byteorder.h>
|
#include <linux/byteorder.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
@ -18,38 +20,66 @@ do { \
|
|||||||
exit(EXIT_FAILURE); \
|
exit(EXIT_FAILURE); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
static inline void *xcalloc(size_t count, size_t size)
|
||||||
|
{
|
||||||
|
void *p = calloc(count, size);
|
||||||
|
|
||||||
|
if (!p)
|
||||||
|
die("insufficient memory");
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *xmalloc(size_t size)
|
||||||
|
{
|
||||||
|
void *p = malloc(size);
|
||||||
|
|
||||||
|
if (!p)
|
||||||
|
die("insufficient memory");
|
||||||
|
|
||||||
|
memset(p, 0, size);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void xpread(int fd, void *buf, size_t count, off_t offset)
|
||||||
|
{
|
||||||
|
ssize_t r = pread(fd, buf, count, offset);
|
||||||
|
|
||||||
|
if (r != count)
|
||||||
|
die("read error (ret %zi)", r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void xpwrite(int fd, const void *buf, size_t count, off_t offset)
|
||||||
|
{
|
||||||
|
ssize_t r = pwrite(fd, buf, count, offset);
|
||||||
|
|
||||||
|
if (r != count)
|
||||||
|
die("write error (ret %zi err %s)", r, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
enum units {
|
enum units {
|
||||||
BYTES,
|
BYTES,
|
||||||
SECTORS,
|
SECTORS,
|
||||||
HUMAN_READABLE,
|
HUMAN_READABLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct units_buf pr_units(u64, enum units);
|
struct units_buf __pr_units(u64, enum units);
|
||||||
|
|
||||||
struct units_buf {
|
struct units_buf {
|
||||||
char b[20];
|
char b[20];
|
||||||
};
|
};
|
||||||
|
|
||||||
long strtoul_or_die(const char *, size_t, const char *);
|
#define pr_units(_v, _u) __pr_units(_v, _u).b
|
||||||
|
|
||||||
u64 hatoi(const char *);
|
|
||||||
unsigned hatoi_validate(const char *, const char *);
|
|
||||||
unsigned nr_args(char * const *);
|
|
||||||
|
|
||||||
char *read_file_str(int, const char *);
|
char *read_file_str(int, const char *);
|
||||||
u64 read_file_u64(int, const char *);
|
u64 read_file_u64(int, const char *);
|
||||||
|
|
||||||
ssize_t read_string_list(const char *, const char * const[]);
|
|
||||||
ssize_t read_string_list_or_die(const char *, const char * const[],
|
ssize_t read_string_list_or_die(const char *, const char * const[],
|
||||||
const char *);
|
const char *);
|
||||||
void print_string_list(const char * const[], size_t);
|
|
||||||
|
|
||||||
u64 get_size(const char *, int);
|
u64 get_size(const char *, int);
|
||||||
unsigned get_blocksize(const char *, int);
|
unsigned get_blocksize(const char *, int);
|
||||||
|
|
||||||
#include "linux/bcache.h"
|
|
||||||
#include "linux/bcache-ioctl.h"
|
|
||||||
|
|
||||||
int bcachectl_open(void);
|
int bcachectl_open(void);
|
||||||
|
|
||||||
struct bcache_handle {
|
struct bcache_handle {
|
||||||
@ -59,6 +89,6 @@ struct bcache_handle {
|
|||||||
|
|
||||||
struct bcache_handle bcache_fs_open(const char *);
|
struct bcache_handle bcache_fs_open(const char *);
|
||||||
|
|
||||||
bool ask_proceed(void);
|
bool ask_yn(void);
|
||||||
|
|
||||||
#endif /* _TOOLS_UTIL_H */
|
#endif /* _TOOLS_UTIL_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user