mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-02-02 00:00:03 +03:00
2831b89a7c
Factor out bch2_pick_bucket_size() from the format code, and pick the bucket size before picking the superblock location - that way we can ensure the superblock gets its own bucket and doesn't trigger warnings due to the allocation code noticing different types of data in the same bucket.
446 lines
11 KiB
C
446 lines
11 KiB
C
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <libgen.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "bcachefs_ioctl.h"
|
|
#include "cmds.h"
|
|
#include "libbcachefs.h"
|
|
#include "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 bcache_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: %s\n", strerror(errno));
|
|
|
|
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, .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"
|
|
"Usage: bcachefs device add [OPTION]... filesystem device\n"
|
|
"\n"
|
|
"Options:\n"
|
|
" --fs_size=size Size of filesystem on device\n"
|
|
" --bucket=size Bucket size\n"
|
|
" --discard Enable discards\n"
|
|
" -t, --tier=# Higher tier (e.g. 1) indicates slower devices\n"
|
|
" -f, --force Use device even if it appears to already be formatted\n"
|
|
" -h, --help Display this help and exit\n"
|
|
"\n"
|
|
"Report bugs to <linux-bcache@vger.kernel.org>");
|
|
}
|
|
|
|
int cmd_device_add(int argc, char *argv[])
|
|
{
|
|
static const struct option longopts[] = {
|
|
{ "fs_size", required_argument, NULL, 'S' },
|
|
{ "bucket", required_argument, NULL, 'B' },
|
|
{ "discard", no_argument, NULL, 'D' },
|
|
{ "tier", required_argument, NULL, 't' },
|
|
{ "force", no_argument, NULL, 'f' },
|
|
{ NULL }
|
|
};
|
|
struct format_opts format_opts = format_opts_default();
|
|
struct dev_opts dev_opts = { 0 };
|
|
bool force = false;
|
|
int opt;
|
|
|
|
while ((opt = getopt_long(argc, argv, "t:fh",
|
|
longopts, NULL)) != -1)
|
|
switch (opt) {
|
|
case 'S':
|
|
if (bch2_strtoull_h(optarg, &dev_opts.size))
|
|
die("invalid filesystem size");
|
|
|
|
dev_opts.size >>= 9;
|
|
break;
|
|
case 'B':
|
|
dev_opts.bucket_size =
|
|
hatoi_validate(optarg, "bucket size");
|
|
break;
|
|
case 'D':
|
|
dev_opts.discard = true;
|
|
break;
|
|
case 't':
|
|
if (kstrtouint(optarg, 10, &dev_opts.tier) ||
|
|
dev_opts.tier >= BCH_TIER_MAX)
|
|
die("invalid tier");
|
|
break;
|
|
case 'f':
|
|
force = true;
|
|
break;
|
|
case 'h':
|
|
device_add_usage();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
if (argc - optind != 2)
|
|
die("Please supply a filesystem and a device to add");
|
|
|
|
struct bcache_handle fs = bcache_fs_open(argv[optind]);
|
|
|
|
dev_opts.path = argv[optind + 1];
|
|
dev_opts.fd = open_for_format(dev_opts.path, force);
|
|
|
|
format_opts.block_size =
|
|
read_file_u64(fs.sysfs_fd, "block_size_bytes") >> 9;
|
|
format_opts.btree_node_size =
|
|
read_file_u64(fs.sysfs_fd, "btree_node_size_bytes") >> 9;
|
|
|
|
struct bch_sb *sb = bch2_format(format_opts, &dev_opts, 1);
|
|
free(sb);
|
|
fsync(dev_opts.fd);
|
|
close(dev_opts.fd);
|
|
|
|
struct bch_ioctl_disk i = { .dev = (__u64) dev_opts.path, };
|
|
|
|
xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_ADD, &i);
|
|
return 0;
|
|
}
|
|
|
|
static void device_remove_usage(void)
|
|
{
|
|
puts("bcachefs device_remove - remove a device from a filesystem\n"
|
|
"Usage: bcachefs device remove filesystem device\n"
|
|
"\n"
|
|
"Options:\n"
|
|
" -f, --force Force removal, even if some data\n"
|
|
" couldn't be migrated\n"
|
|
" --force-metadata Force removal, even if some metadata\n"
|
|
" couldn't be migrated\n"
|
|
" -h, --help display this help and exit\n"
|
|
"Report bugs to <linux-bcache@vger.kernel.org>");
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
int cmd_device_remove(int argc, char *argv[])
|
|
{
|
|
static const struct option longopts[] = {
|
|
{ "force", 0, NULL, 'f' },
|
|
{ "force-metadata", 0, NULL, 'F' },
|
|
{ "help", 0, NULL, 'h' },
|
|
{ NULL }
|
|
};
|
|
int opt, flags = 0;
|
|
|
|
while ((opt = getopt_long(argc, argv, "fh", longopts, NULL)) != -1)
|
|
switch (opt) {
|
|
case 'f':
|
|
flags |= BCH_FORCE_IF_DATA_LOST;
|
|
break;
|
|
case 'F':
|
|
flags |= BCH_FORCE_IF_METADATA_LOST;
|
|
break;
|
|
case 'h':
|
|
device_remove_usage();
|
|
}
|
|
|
|
if (argc - optind != 2)
|
|
die("Please supply a filesystem and at least one device to remove");
|
|
|
|
disk_ioctl(argv[optind], argv[optind + 1],
|
|
BCH_IOCTL_DISK_REMOVE, 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"
|
|
"\n"
|
|
"Options:\n"
|
|
" -h, --help Display this help and exit\n"
|
|
"\n"
|
|
"Report bugs to <linux-bcache@vger.kernel.org>");
|
|
}
|
|
|
|
int cmd_device_online(int argc, char *argv[])
|
|
{
|
|
int opt;
|
|
|
|
while ((opt = getopt(argc, argv, "h")) != -1)
|
|
switch (opt) {
|
|
case 'h':
|
|
device_online_usage();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
if (argc - optind != 2)
|
|
die("Please supply a filesystem and a device");
|
|
|
|
disk_ioctl(argv[optind], argv[optind + 1], BCH_IOCTL_DISK_ONLINE, 0);
|
|
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"
|
|
"\n"
|
|
"Options:\n"
|
|
" -f, --force Force, if data redundancy will be degraded\n"
|
|
" -h, --help Display this help and exit\n"
|
|
"\n"
|
|
"Report bugs to <linux-bcache@vger.kernel.org>");
|
|
}
|
|
|
|
int cmd_device_offline(int argc, char *argv[])
|
|
{
|
|
static const struct option longopts[] = {
|
|
{ "force", 0, NULL, 'f' },
|
|
{ NULL }
|
|
};
|
|
int opt, flags = 0;
|
|
|
|
while ((opt = getopt_long(argc, argv, "fh",
|
|
longopts, NULL)) != -1)
|
|
switch (opt) {
|
|
case 'f':
|
|
flags |= BCH_FORCE_IF_DEGRADED;
|
|
break;
|
|
case 'h':
|
|
device_offline_usage();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
if (argc - optind != 2)
|
|
die("Please supply a filesystem and a device");
|
|
|
|
disk_ioctl(argv[optind], argv[optind + 1],
|
|
BCH_IOCTL_DISK_OFFLINE, 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"
|
|
"\n"
|
|
"Options:\n"
|
|
" -h, --help Display this help and exit\n"
|
|
"\n"
|
|
"Report bugs to <linux-bcache@vger.kernel.org>");
|
|
}
|
|
|
|
int cmd_device_evacuate(int argc, char *argv[])
|
|
{
|
|
int opt;
|
|
|
|
while ((opt = getopt(argc, argv, "h")) != -1)
|
|
switch (opt) {
|
|
case 'h':
|
|
device_evacuate_usage();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
if (argc - optind != 2)
|
|
die("Please supply a filesystem and a device");
|
|
|
|
disk_ioctl(argv[optind], argv[optind + 1], BCH_IOCTL_DISK_EVACUATE, 0);
|
|
return 0;
|
|
}
|
|
|
|
static void device_set_state_usage(void)
|
|
{
|
|
puts("bcachefs device set-state\n"
|
|
"Usage: bcachefs device set-state filesystem device new-state\n"
|
|
"\n"
|
|
"Options:\n"
|
|
" -f, --force Force, if data redundancy will be degraded\n"
|
|
" -h, --help display this help and exit\n"
|
|
"Report bugs to <linux-bcache@vger.kernel.org>");
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
int cmd_device_set_state(int argc, char *argv[])
|
|
{
|
|
static const struct option longopts[] = {
|
|
{ "force", 0, NULL, 'f' },
|
|
{ "help", 0, NULL, 'h' },
|
|
{ NULL }
|
|
};
|
|
int opt, flags = 0;
|
|
|
|
while ((opt = getopt_long(argc, argv, "fh", longopts, NULL)) != -1)
|
|
switch (opt) {
|
|
case 'f':
|
|
flags |= BCH_FORCE_IF_DEGRADED;
|
|
break;
|
|
case 'h':
|
|
device_set_state_usage();
|
|
}
|
|
|
|
if (argc - optind != 3)
|
|
die("Please supply a filesystem, device and state");
|
|
|
|
struct bcache_handle fs = bcache_fs_open(argv[optind]);
|
|
|
|
struct bch_ioctl_disk_set_state i = {
|
|
.flags = flags,
|
|
.dev = (__u64) argv[optind + 1],
|
|
.new_state = read_string_list_or_die(argv[optind + 2],
|
|
bch2_dev_state, "device state"),
|
|
};
|
|
|
|
xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_SET_STATE, &i);
|
|
return 0;
|
|
}
|