New data rereplicate command

This commit is contained in:
Kent Overstreet 2018-02-08 15:30:19 -05:00
parent 6976570d67
commit 7875b82630
12 changed files with 296 additions and 218 deletions

View File

@ -53,6 +53,9 @@ static void usage(void)
" device set-state Mark a device as failed\n"
" device resize Resize filesystem on a device\n"
"\n"
"Commands for managing filesystem data:\n"
" data rereplicate Rereplicate degraded data\n"
"\n"
"Encryption:\n"
" unlock Unlock an encrypted filesystem prior to running/mounting\n"
" set-passphrase Change passphrase on an existing (unmounted) filesystem\n"
@ -120,6 +123,17 @@ static int device_cmds(int argc, char *argv[])
return 0;
}
static int data_cmds(int argc, char *argv[])
{
char *cmd = pop_cmd(&argc, argv);
if (!strcmp(cmd, "rereplicate"))
return cmd_data_rereplicate(argc, argv);
usage();
return 0;
}
int main(int argc, char *argv[])
{
full_cmd = argv[0];
@ -151,6 +165,9 @@ int main(int argc, char *argv[])
if (!strcmp(cmd, "device"))
return device_cmds(argc, argv);
if (!strcmp(cmd, "data"))
return data_cmds(argc, argv);
if (!strcmp(cmd, "unlock"))
return cmd_unlock(argc, argv);
if (!strcmp(cmd, "set-passphrase"))

48
cmd_data.c Normal file
View File

@ -0,0 +1,48 @@
#include <stdio.h>
#include <sys/ioctl.h>
#include "libbcachefs/bcachefs_ioctl.h"
#include "cmds.h"
#include "libbcachefs.h"
static void data_rereplicate_usage(void)
{
puts("bcachefs data rereplicate\n"
"Usage: bcachefs data rereplicate filesystem\n"
"\n"
"Walks existing data in a filesystem, writing additional copies\n"
"of any degraded data\n"
"\n"
"Options:\n"
" -h, --help display this help and exit\n"
"Report bugs to <linux-bcache@vger.kernel.org>");
exit(EXIT_SUCCESS);
}
int cmd_data_rereplicate(int argc, char *argv[])
{
int opt;
while ((opt = getopt(argc, argv, "h")) != -1)
switch (opt) {
case 'h':
data_rereplicate_usage();
}
args_shift(optind);
char *fs_path = arg_pop();
if (!fs_path)
die("Please supply a filesystem");
if (argc)
die("too many arguments");
return bchu_data(bcache_fs_open(fs_path), (struct bch_ioctl_data) {
.op = BCH_DATA_OP_REREPLICATE,
.start = POS_MIN,
.end = POS_MAX,
});
}

View File

@ -103,16 +103,17 @@ int cmd_dump(int argc, char *argv[])
dump_usage();
exit(EXIT_SUCCESS);
}
if (optind >= argc)
die("Please supply device(s) to check");
args_shift(optind);
if (!out)
die("Please supply output filename");
struct bch_fs *c = bch2_fs_open(argv + optind, argc - optind, opts);
if (!argc)
die("Please supply device(s) to check");
struct bch_fs *c = bch2_fs_open(argv, argc, opts);
if (IS_ERR(c))
die("error opening %s: %s", argv[optind], strerror(-PTR_ERR(c)));
die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
down_read(&c->gc_lock);
@ -299,13 +300,14 @@ int cmd_list(int argc, char *argv[])
list_keys_usage();
exit(EXIT_SUCCESS);
}
args_shift(optind);
if (optind >= argc)
die("Please supply device(s) to check");
if (!argc)
die("Please supply device(s)");
struct bch_fs *c = bch2_fs_open(argv + optind, argc - optind, opts);
struct bch_fs *c = bch2_fs_open(argv, argc, opts);
if (IS_ERR(c))
die("error opening %s: %s", argv[optind], strerror(-PTR_ERR(c)));
die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
switch (mode) {
case 0:

View File

@ -19,165 +19,6 @@
#include "libbcachefs/opts.h"
#include "tools-util.h"
/* This code belongs under show_fs */
#if 0
struct bcache_dev {
unsigned nr;
const char *name;
unsigned has_metadata;
unsigned has_data;
const char *state;
unsigned tier;
u64 bucket_size;
u64 first_bucket;
u64 nbuckets;
/* XXX: dirty != used, it doesn't count metadata */
u64 bytes_dirty;
};
static struct bcache_dev fill_dev(const char *dev_name, unsigned nr, int dir)
{
return (struct bcache_dev) {
.nr = nr,
.name = dev_name,
.has_metadata = read_file_u64(dir, "has_metadata"),
.has_data = read_file_u64(dir, "has_data"),
.state = read_file_str(dir, "state"),
.tier = read_file_u64(dir, "tier"),
.bucket_size = read_file_u64(dir, "bucket_size_bytes"),
.first_bucket = read_file_u64(dir, "first_bucket"),
.nbuckets = read_file_u64(dir, "nbuckets"),
.bytes_dirty = read_file_u64(dir, "dirty_bytes"),
};
}
static void show_dev(struct bcache_dev *dev)
{
u64 capacity = (dev->nbuckets - dev->first_bucket) *
dev->bucket_size;
/*
* XXX: show fragmentation information, cached/dirty information
*/
printf("Device %u (/dev/%s):\n"
" Has metadata:\t%u\n"
" Has dirty data:\t%u\n"
" State:\t\t%s\n"
" Tier:\t\t%u\n"
" Size:\t\t%llu\n"
" Used:\t\t%llu\n"
" Free:\t\t%llu\n"
" Use%%:\t\t%llu\n",
dev->nr, dev->name,
dev->has_metadata,
dev->has_data,
dev->state,
dev->tier,
capacity,
dev->bytes_dirty,
capacity - dev->bytes_dirty,
(dev->bytes_dirty * 100) / capacity);
}
int cmd_device_show(int argc, char *argv[])
{
int human_readable = 0;
NihOption opts[] = {
// { int shortoption, char *longoption, char *help, NihOptionGroup, char *argname, void *value, NihOptionSetter}
{ 'h', "human-readable", N_("print sizes in powers of 1024 (e.g., 1023M)"),
NULL, NULL, &human_readable, NULL},
NIH_OPTION_LAST
};
char **args = bch_nih_init(argc, argv, opts);
if (argc != 2)
die("Please supply a single device");
struct bchfs_handle fs = bcache_fs_open(argv[1]);
struct dirent *entry;
struct bcache_dev devices[256];
unsigned i, j, nr_devices = 0, nr_active_tiers = 0;
unsigned tiers[BCH_TIER_MAX]; /* number of devices in each tier */
memset(tiers, 0, sizeof(tiers));
while ((entry = readdir(fs.sysfs))) {
unsigned nr;
int pos = 0;
sscanf(entry->d_name, "cache%u%n", &nr, &pos);
if (pos != strlen(entry->d_name))
continue;
char link[PATH_MAX];
if (readlinkat(dirfd(fs.sysfs), entry->d_name,
link, sizeof(link)) < 0)
die("readlink error: %m\n");
char *dev_name = basename(dirname(link));
int fd = xopenat(dirfd(fs.sysfs), entry->d_name, O_RDONLY);
devices[nr_devices] = fill_dev(strdup(dev_name), nr, fd);
tiers[devices[nr_devices].tier]++;
nr_devices++;
close(fd);
}
for (i = 0; i < BCH_TIER_MAX; i++)
if (tiers[i])
nr_active_tiers++;
/* Print out devices sorted by tier: */
bool first = true;
for (i = 0; i < BCH_TIER_MAX; i++) {
if (!tiers[i])
continue;
if (nr_active_tiers > 1) {
if (!first)
printf("\n");
first = false;
printf("Tier %u:\n\n", i);
}
for (j = 0; j < nr_devices; j++) {
if (devices[j].tier != i)
continue;
if (!first)
printf("\n");
first = false;
show_dev(&devices[j]);
}
}
return 0;
}
#endif
static void disk_ioctl(const char *fs, const char *dev, int cmd, int flags)
{
struct bch_ioctl_disk i = { .flags = flags, };
if (!kstrtoull(dev, 10, &i.dev))
i.flags |= BCH_BY_INDEX;
else
i.dev = (u64) dev;
xioctl(bcache_fs_open(fs).ioctl_fd, cmd, &i);
}
static void device_add_usage(void)
{
puts("bcachefs device add - add a device to an existing filesystem\n"
@ -239,12 +80,20 @@ int cmd_device_add(int argc, char *argv[])
}
args_shift(optind);
if (argc != 2)
die("Please supply a filesystem and a device to add");
char *fs_path = arg_pop();
if (!fs_path)
die("Please supply a filesystem");
struct bchfs_handle fs = bcache_fs_open(arg_pop());
char *dev_path = arg_pop();
if (!dev_path)
die("Please supply a device");
dev_opts.path = arg_pop();
if (argc)
die("too many arguments");
struct bchfs_handle fs = bcache_fs_open(fs_path);
dev_opts.path = dev_path;
dev_opts.fd = open_for_format(dev_opts.path, force);
format_opts.block_size =
@ -264,7 +113,7 @@ int cmd_device_add(int argc, char *argv[])
static void device_remove_usage(void)
{
puts("bcachefs device_remove - remove a device from a filesystem\n"
"Usage: bcachefs device remove filesystem device\n"
"Usage: bcachefs device remove device\n"
"\n"
"Options:\n"
" -f, --force Force removal, even if some data\n"
@ -299,10 +148,6 @@ int cmd_device_remove(int argc, char *argv[])
}
args_shift(optind);
char *fs = arg_pop();
if (!fs)
die("Please supply a filesystem");
char *dev = arg_pop();
if (!dev)
die("Please supply a device to remove");
@ -310,14 +155,16 @@ int cmd_device_remove(int argc, char *argv[])
if (argc)
die("too many arguments");
disk_ioctl(fs, dev, BCH_IOCTL_DISK_REMOVE, flags);
unsigned dev_idx;
struct bchfs_handle fs = bchu_fs_open_by_dev(dev, &dev_idx);
bchu_disk_remove(fs, dev_idx, flags);
return 0;
}
static void device_online_usage(void)
{
puts("bcachefs device online - readd a device to a running filesystem\n"
"Usage: bcachefs device online [OPTION]... filesystem device\n"
"Usage: bcachefs device online [OPTION]... device\n"
"\n"
"Options:\n"
" -h, --help Display this help and exit\n"
@ -335,18 +182,25 @@ int cmd_device_online(int argc, char *argv[])
device_online_usage();
exit(EXIT_SUCCESS);
}
args_shift(optind);
if (argc - optind != 2)
die("Please supply a filesystem and a device");
char *dev = arg_pop();
if (!dev)
die("Please supply a device");
disk_ioctl(argv[optind], argv[optind + 1], BCH_IOCTL_DISK_ONLINE, 0);
if (argc)
die("too many arguments");
unsigned dev_idx;
struct bchfs_handle fs = bchu_fs_open_by_dev(dev, &dev_idx);
bchu_disk_online(fs, dev);
return 0;
}
static void device_offline_usage(void)
{
puts("bcachefs device offline - take a device offline, without removing it\n"
"Usage: bcachefs device offline [OPTION]... filesystem device\n"
"Usage: bcachefs device offline [OPTION]... device\n"
"\n"
"Options:\n"
" -f, --force Force, if data redundancy will be degraded\n"
@ -373,19 +227,25 @@ int cmd_device_offline(int argc, char *argv[])
device_offline_usage();
exit(EXIT_SUCCESS);
}
args_shift(optind);
if (argc - optind != 2)
die("Please supply a filesystem and a device");
char *dev = arg_pop();
if (!dev)
die("Please supply a device");
disk_ioctl(argv[optind], argv[optind + 1],
BCH_IOCTL_DISK_OFFLINE, flags);
if (argc)
die("too many arguments");
unsigned dev_idx;
struct bchfs_handle fs = bchu_fs_open_by_dev(dev, &dev_idx);
bchu_disk_offline(fs, dev_idx, flags);
return 0;
}
static void device_evacuate_usage(void)
{
puts("bcachefs device evacuate - move data off of a given device\n"
"Usage: bcachefs device evacuate [OPTION]... filesystem device\n"
"Usage: bcachefs device evacuate [OPTION]... device\n"
"\n"
"Options:\n"
" -h, --help Display this help and exit\n"
@ -403,18 +263,30 @@ int cmd_device_evacuate(int argc, char *argv[])
device_evacuate_usage();
exit(EXIT_SUCCESS);
}
args_shift(optind);
if (argc - optind != 2)
die("Please supply a filesystem and a device");
char *dev_path = arg_pop();
if (!dev_path)
die("Please supply a device");
disk_ioctl(argv[optind], argv[optind + 1], BCH_IOCTL_DISK_EVACUATE, 0);
return 0;
if (argc)
die("too many arguments");
unsigned dev_idx;
struct bchfs_handle fs = bchu_fs_open_by_dev(dev_path, &dev_idx);
return bchu_data(fs, (struct bch_ioctl_data) {
.op = BCH_DATA_OP_MIGRATE,
.start = POS_MIN,
.end = POS_MAX,
.migrate.dev = dev_idx,
});
}
static void device_set_state_usage(void)
{
puts("bcachefs device set-state\n"
"Usage: bcachefs device set-state filesystem device new-state\n"
"Usage: bcachefs device set-state device new-state\n"
"\n"
"Options:\n"
" -f, --force Force, if data redundancy will be degraded\n"
@ -440,16 +312,23 @@ int cmd_device_set_state(int argc, char *argv[])
case 'h':
device_set_state_usage();
}
args_shift(optind);
if (argc - optind != 3)
die("Please supply a filesystem, device and state");
char *dev_path = arg_pop();
if (!dev_path)
die("Please supply a device");
struct bchfs_handle fs = bcache_fs_open(argv[optind]);
char *new_state_str = arg_pop();
if (!new_state_str)
die("Please supply a device state");
bchu_disk_set_state(fs, argv[optind + 1],
read_string_list_or_die(argv[optind + 2],
bch2_dev_state, "device state"),
flags);
unsigned new_state = read_string_list_or_die(new_state_str,
bch2_dev_state, "device state");
unsigned dev_idx;
struct bchfs_handle fs = bchu_fs_open_by_dev(dev_path, &dev_idx);
bchu_disk_set_state(fs, dev_idx, new_state, flags);
return 0;
}

View File

@ -329,6 +329,10 @@ int cmd_show_super(int argc, char *argv[])
die("too many arguments");
struct bch_opts opts = bch2_opts_empty();
opt_set(opts, noexcl, true);
opt_set(opts, nochanges, true);
struct bch_sb_handle sb;
int ret = bch2_read_super(dev, &opts, &sb);
if (ret)

View File

@ -80,7 +80,7 @@ static void print_fs_usage(const char *path, enum units units)
int cmd_fs_usage(int argc, char *argv[])
{
enum units units = BYTES;
unsigned i;
char *fs;
int opt;
while ((opt = getopt(argc, argv, "h")) != -1)
@ -89,12 +89,13 @@ int cmd_fs_usage(int argc, char *argv[])
units = HUMAN_READABLE;
break;
}
args_shift(optind);
if (argc - optind < 1) {
if (!argc) {
print_fs_usage(".", units);
} else {
for (i = optind; i < argc; i++)
print_fs_usage(argv[i], units);
while ((fs = arg_pop()))
print_fs_usage(fs, units);
}
return 0;

View File

@ -50,13 +50,14 @@ int cmd_fsck(int argc, char *argv[])
usage();
exit(EXIT_SUCCESS);
}
args_shift(optind);
if (optind >= argc)
if (!argc)
die("Please supply device(s) to check");
struct bch_fs *c = bch2_fs_open(argv + optind, argc - optind, opts);
struct bch_fs *c = bch2_fs_open(argv, argc, opts);
if (IS_ERR(c))
die("error opening %s: %s", argv[optind], strerror(-PTR_ERR(c)));
die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
bch2_fs_stop(c);
return 0;

2
cmds.h
View File

@ -27,6 +27,8 @@ int cmd_device_evacuate(int argc, char *argv[]);
int cmd_device_set_state(int argc, char *argv[]);
int cmd_device_resize(int argc, char *argv[]);
int cmd_data_rereplicate(int argc, char *argv[]);
int cmd_unlock(int argc, char *argv[]);
int cmd_set_passphrase(int argc, char *argv[]);
int cmd_remove_passphrase(int argc, char *argv[]);

View File

@ -7,6 +7,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
@ -17,6 +18,7 @@
#include "libbcachefs/checksum.h"
#include "crypto.h"
#include "libbcachefs.h"
#include "libbcachefs/btree_cache.h"
#include "libbcachefs/opts.h"
#include "libbcachefs/super-io.h"
@ -640,7 +642,7 @@ struct bchfs_handle bcache_fs_open(const char *path)
if (!uuid_parse(path, ret.uuid.b)) {
/* It's a UUID, look it up in sysfs: */
char *sysfs = mprintf("%s%s", SYSFS_BASE, path);
char *sysfs = mprintf(SYSFS_BASE "%s", path);
ret.sysfs_fd = xopen(sysfs, O_RDONLY);
char *minor = read_file_str(ret.sysfs_fd, "minor");
@ -662,10 +664,94 @@ struct bchfs_handle bcache_fs_open(const char *path)
char uuid_str[40];
uuid_unparse(uuid.uuid.b, uuid_str);
char *sysfs = mprintf("%s%s", SYSFS_BASE, uuid_str);
char *sysfs = mprintf(SYSFS_BASE "%s", uuid_str);
ret.sysfs_fd = xopen(sysfs, O_RDONLY);
free(sysfs);
}
return ret;
}
/*
* Given a path to a block device, open the filesystem it belongs to; also
* return the device's idx:
*/
struct bchfs_handle bchu_fs_open_by_dev(const char *path, unsigned *idx)
{
char buf[1024], *uuid_str;
struct stat stat = xstat(path);
if (!S_ISBLK(stat.st_mode))
die("%s is not a block device", path);
char *sysfs = mprintf("/sys/dev/block/%u:%u/bcachefs",
major(stat.st_dev),
minor(stat.st_dev));
ssize_t len = readlink(sysfs, buf, sizeof(buf));
free(sysfs);
if (len > 0) {
char *p = strrchr(buf, '/');
if (!p || sscanf(p + 1, "dev-%u", idx) != 1)
die("error parsing sysfs");
*p = '\0';
p = strrchr(buf, '/');
uuid_str = p + 1;
} else {
struct bch_opts opts = bch2_opts_empty();
opt_set(opts, noexcl, true);
opt_set(opts, nochanges, true);
struct bch_sb_handle sb;
int ret = bch2_read_super(path, &opts, &sb);
if (ret)
die("Error opening %s: %s", path, strerror(-ret));
*idx = sb.sb->dev_idx;
uuid_str = buf;
uuid_unparse(sb.sb->user_uuid.b, uuid_str);
bch2_free_super(&sb);
}
return bcache_fs_open(uuid_str);
}
int bchu_data(struct bchfs_handle fs, struct bch_ioctl_data cmd)
{
int progress_fd = xioctl(fs.ioctl_fd, BCH_IOCTL_DATA, &cmd);
while (1) {
struct bch_ioctl_data_progress p;
if (read(progress_fd, &p, sizeof(p)) != sizeof(p))
die("error reading from progress fd");
if (p.data_type == U8_MAX)
break;
printf("\33[2K\r");
printf("%llu%% complete: current position %s",
p.sectors_done * 100 / p.sectors_total,
bch2_data_types[p.data_type]);
switch (p.data_type) {
case BCH_DATA_BTREE:
case BCH_DATA_USER:
printf(" %s:%llu:%llu",
bch2_btree_ids[p.btree_id],
p.pos.inode,
p.pos.offset);
}
sleep(1);
}
printf("\nDone\n");
close(progress_fd);
return 0;
}

View File

@ -89,6 +89,7 @@ struct bchfs_handle {
void bcache_fs_close(struct bchfs_handle);
struct bchfs_handle bcache_fs_open(const char *);
struct bchfs_handle bchu_fs_open_by_dev(const char *, unsigned *);
static inline void bchu_disk_add(struct bchfs_handle fs, char *dev)
{
@ -97,19 +98,44 @@ static inline void bchu_disk_add(struct bchfs_handle fs, char *dev)
xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_ADD, &i);
}
static inline void bchu_disk_set_state(struct bchfs_handle fs, const char *dev,
static inline void bchu_disk_remove(struct bchfs_handle fs, unsigned dev_idx,
unsigned flags)
{
struct bch_ioctl_disk i = {
.flags = flags|BCH_BY_INDEX,
.dev = dev_idx,
};
xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_REMOVE, &i);
}
static inline void bchu_disk_online(struct bchfs_handle fs, char *dev)
{
struct bch_ioctl_disk i = { .dev = (__u64) dev, };
xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_ONLINE, &i);
}
static inline void bchu_disk_offline(struct bchfs_handle fs, unsigned dev_idx,
unsigned flags)
{
struct bch_ioctl_disk i = {
.flags = flags|BCH_BY_INDEX,
.dev = dev_idx,
};
xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_OFFLINE, &i);
}
static inline void bchu_disk_set_state(struct bchfs_handle fs, unsigned dev,
unsigned new_state, unsigned flags)
{
struct bch_ioctl_disk_set_state i = {
.flags = flags,
.flags = flags|BCH_BY_INDEX,
.new_state = new_state,
.dev = dev,
};
if (!kstrtoull(dev, 10, &i.dev))
i.flags |= BCH_BY_INDEX;
else
i.dev = (u64) dev;
xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_SET_STATE, &i);
}
@ -176,4 +202,6 @@ static inline void bchu_disk_resize(struct bchfs_handle fs,
xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_RESIZE, &i);
}
int bchu_data(struct bchfs_handle, struct bch_ioctl_data);
#endif /* _LIBBCACHE_H */

View File

@ -118,6 +118,14 @@ struct stat xfstat(int fd)
return stat;
}
struct stat xstat(const char *path)
{
struct stat statbuf;
if (stat(path, &statbuf))
die("stat error: %m");
return statbuf;
}
/* Formatting: */
int printf_pad(unsigned pad, const char * fmt, ...)

View File

@ -28,6 +28,7 @@ void xpread(int, void *, size_t, off_t);
void xpwrite(int, const void *, size_t, off_t);
struct stat xfstatat(int, const char *, int);
struct stat xfstat(int);
struct stat xstat(const char *);
#define xopenat(_dirfd, _path, ...) \
({ \
@ -155,8 +156,9 @@ char *dev_to_mount(char *);
#define args_shift(_nr) \
do { \
argc -= (_nr); \
argv += (_nr); \
unsigned _n = min((_nr), argc); \
argc -= _n; \
argv += _n; \
} while (0)
#define arg_pop() \