-BCACHE_REVISION=5548432e689033ee93f0835b41571f8ec8b7bc48
+BCACHE_REVISION=3ea79179e3101fb50de8730a809d00d189f05be5
puts("bcache - tool for managing bcache volumes/filesystems\n"
"usage: bcache <command> [<args>]\n"
"\n"
- "Commands for formatting, startup and shutdown:\n"
- " format Format a new filesystem\n"
- " unlock Unlock an encrypted filesystem prior to running/mounting\n"
- " assemble Assemble an existing multi device filesystem\n"
- " incremental Incrementally assemble an existing multi device filesystem\n"
- " run Start a partially assembled filesystem\n"
- " stop Stop a running filesystem\n"
+ "Superblock commands:\n"
+ " format Format a new filesystem\n"
+ " show-super Dump superblock information to stdout\n"
"\n"
+ "Repair:\n"
+ " bcache fsck Check an existing filesystem for errors\n"
+ "\n"
+ "Startup/shutdown, assembly of multi device filesystems:\n"
+ " unlock Unlock an encrypted filesystem prior to running/mounting\n"
+ " assemble Assemble an existing multi device filesystem\n"
+ " incremental Incrementally assemble an existing multi device filesystem\n"
+ " run Start a partially assembled filesystem\n"
+ " stop Stop a running filesystem\n"
+
"Commands for managing a running filesystem:\n"
- " fs_show Show various information about a filesystem\n"
- " fs_set Modify filesystem options\n"
+ " fs show Show various information about a filesystem\n"
+ " fs set Modify filesystem options\n"
"\n"
- "Commands for managing a specific device in a filesystem:\n"
- " device_show Show information about a formatted device\n"
- " device_add Add a device to an existing (running) filesystem\n"
- " device_fail Mark a device as failed\n"
- " device_remove Remove a device from an existing (running) filesystem\n"
+ "Commands for managing devices within a running filesystem:\n"
+ " device add Add a new device to an existing filesystem\n"
+ " device remove Remove a device from an existing filesystem\n"
+ " device online Readd an existing member to a filesystem\n"
+ " device offline Take a device offline, without removing it\n"
+ " device evacuate Migrate data off of a specific device\n"
+ " device set-state Mark a device as failed\n"
"\n"
- "Repair:\n"
- " bcache fsck Check an existing filesystem for errors\n"
+ "Migrate:\n"
+ " migrate Migrate an existing filesystem to bcachefs, in place\n"
+ " migrate-superblock\n"
+ " Add default superblock, after bcache migrate\n"
"\n"
"Debug:\n"
- " bcache dump Dump filesystem metadata to a qcow2 image\n"
- " bcache list List filesystem metadata in textual form\n"
- "\n"
- "Migrate:\n"
- " bcache migrate Migrate an existing filesystem to bcachefs, in place\n"
- " bcache migrate_superblock\n"
- " Add default superblock, after bcache migrate\n");
+ "These commands work on offline, unmounted filesystems\n"
+ " dump Dump filesystem metadata to a qcow2 image\n"
+ " list List filesystem metadata in textual form\n");
}
-int main(int argc, char *argv[])
-{
- char *cmd;
-
- setvbuf(stdout, NULL, _IOLBF, 0);
+static char *full_cmd;
- if (argc < 2) {
+static char *pop_cmd(int *argc, char *argv[])
+{
+ if (*argc < 2) {
printf("%s: missing command\n", argv[0]);
usage();
exit(EXIT_FAILURE);
}
- cmd = argv[1];
+ char *cmd = argv[1];
+ memmove(&argv[1], &argv[2], *argc * sizeof(argv[0]));
+ (*argc)--;
+
+ full_cmd = mprintf("%s %s", full_cmd, cmd);
+ return cmd;
+}
+
+static int fs_cmds(int argc, char *argv[])
+{
+ char *cmd = pop_cmd(&argc, argv);
+
+ if (!strcmp(cmd, "show"))
+ return cmd_fs_show(argc, argv);
+ if (!strcmp(cmd, "set"))
+ return cmd_fs_set(argc, argv);
- memmove(&argv[1], &argv[2], argc * sizeof(argv[0]));
- argc--;
+ usage();
+ return 0;
+}
+
+static int device_cmds(int argc, char *argv[])
+{
+ char *cmd = pop_cmd(&argc, argv);
+
+ if (!strcmp(cmd, "add"))
+ return cmd_device_add(argc, argv);
+ if (!strcmp(cmd, "remove"))
+ return cmd_device_remove(argc, argv);
+ if (!strcmp(cmd, "online"))
+ return cmd_device_online(argc, argv);
+ if (!strcmp(cmd, "offline"))
+ return cmd_device_offline(argc, argv);
+ if (!strcmp(cmd, "evacuate"))
+ return cmd_device_offline(argc, argv);
+ if (!strcmp(cmd, "set-state"))
+ return cmd_device_set_state(argc, argv);
+
+ usage();
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ full_cmd = argv[0];
+
+ setvbuf(stdout, NULL, _IOLBF, 0);
+
+ char *cmd = pop_cmd(&argc, argv);
if (!strcmp(cmd, "format"))
return cmd_format(argc, argv);
+ if (!strcmp(cmd, "show-super"))
+ return cmd_show_super(argc, argv);
+
+ if (!strcmp(cmd, "fsck"))
+ return cmd_fsck(argc, argv);
+
+ if (!strcmp(cmd, "unlock"))
+ return cmd_unlock(argc, argv);
if (!strcmp(cmd, "assemble"))
return cmd_assemble(argc, argv);
if (!strcmp(cmd, "incremental"))
if (!strcmp(cmd, "stop"))
return cmd_stop(argc, argv);
- if (!strcmp(cmd, "fs_show"))
- return cmd_fs_show(argc, argv);
- if (!strcmp(cmd, "fs_set"))
- return cmd_fs_set(argc, argv);
+ if (!strcmp(cmd, "fs"))
+ return fs_cmds(argc, argv);
- if (!strcmp(cmd, "device_show"))
- return cmd_device_show(argc, argv);
- if (!strcmp(cmd, "device_add"))
- return cmd_device_add(argc, argv);
- if (!strcmp(cmd, "device_fail"))
- return cmd_device_fail(argc, argv);
- if (!strcmp(cmd, "device_remove"))
- return cmd_device_remove(argc, argv);
+ if (!strcmp(cmd, "device"))
+ return device_cmds(argc, argv);
- if (!strcmp(cmd, "fsck"))
- return cmd_fsck(argc, argv);
-
- if (!strcmp(cmd, "unlock"))
- return cmd_unlock(argc, argv);
+ if (!strcmp(cmd, "migrate"))
+ return cmd_migrate(argc, argv);
+ if (!strcmp(cmd, "migrate-superblock"))
+ return cmd_migrate_superblock(argc, argv);
if (!strcmp(cmd, "dump"))
return cmd_dump(argc, argv);
if (!strcmp(cmd, "list"))
return cmd_list(argc, argv);
- if (!strcmp(cmd, "migrate"))
- return cmd_migrate(argc, argv);
- if (!strcmp(cmd, "migrate_superblock"))
- return cmd_migrate_superblock(argc, argv);
-
usage();
return 0;
}
#include "cmds.h"
#include "libbcache.h"
#include "linux/bcache-ioctl.h"
+#include "opts.h"
#include "tools-util.h"
/* This code belongs under show_fs */
}
#endif
-int cmd_device_show(int argc, char *argv[])
+static void disk_ioctl(const char *fs, const char *dev, int cmd, int flags)
{
- struct bch_sb *sb;
-
- if (argc != 2)
- die("please supply a single device");
-
- sb = bcache_super_read(argv[1]);
- bcache_super_print(sb, HUMAN_READABLE);
+ struct bch_ioctl_disk i = { .flags = flags, .dev = (__u64) dev, };
- return 0;
+ xioctl(bcache_fs_open(fs).ioctl_fd, cmd, &i);
}
static void device_add_usage(void)
{
- puts("bcache device_add - add a device to an existing filesystem\n"
- "Usage: bcache device_add [OPTION]... filesystem device\n"
+ puts("bcache device add - add a device to an existing filesystem\n"
+ "Usage: bcache device add [OPTION]... filesystem device\n"
"\n"
"Options:\n"
" --fs_size=size Size of filesystem on device\n"
"Report bugs to <linux-bcache@vger.kernel.org>");
}
-static const struct option device_add_opts[] = {
- { "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 }
-};
-
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",
- device_add_opts, NULL)) != -1)
+ longopts, NULL)) != -1)
switch (opt) {
case 'S':
if (bch_strtoull_h(optarg, &dev_opts.size))
fsync(dev_opts.fd);
close(dev_opts.fd);
- struct bch_ioctl_disk_add ia = {
- .dev = (__u64) dev_opts.path,
- };
-
- xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_ADD, &ia);
+ struct bch_ioctl_disk i = { .dev = (__u64) dev_opts.path, };
+ xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_ADD, &i);
return 0;
}
-static void device_fail_usage(void)
+static void device_remove_usage(void)
{
- puts("bcache device_fail - mark a device as failed\n"
- "Usage: bcache device_fail filesystem [devices]\n"
+ puts("bcache device_remove - remove a device from a filesystem\n"
+ "Usage: bcache device remove filesystem device\n"
"\n"
"Options:\n"
" -f, --force Force removal, even if some data\n"
exit(EXIT_SUCCESS);
}
-int cmd_device_fail(int argc, char *argv[])
+int cmd_device_remove(int argc, char *argv[])
{
static const struct option longopts[] = {
- { "force-degraded", 0, NULL, 'f' },
- //{ "force-data-lost", 0, NULL, 'F' },
- //{ "force-metadata-lost", 0, NULL, 'F' },
- { "help", 0, NULL, 'h' },
+ { "force", 0, NULL, 'f' },
+ { "force-metadata", 0, NULL, 'F' },
+ { "help", 0, NULL, 'h' },
{ NULL }
};
- int opt, force_degraded = 0, force_data = 0, force_metadata = 0;
+ int opt, flags = 0;
while ((opt = getopt_long(argc, argv, "fh", longopts, NULL)) != -1)
switch (opt) {
case 'f':
- force_degraded = 1;
+ flags |= BCH_FORCE_IF_DATA_LOST;
+ break;
+ case 'F':
+ flags |= BCH_FORCE_IF_METADATA_LOST;
break;
case 'h':
- device_fail_usage();
+ device_remove_usage();
}
- if (argc - optind < 2)
- die("Please supply a filesystem and at least one device to fail");
+ if (argc - optind != 2)
+ die("Please supply a filesystem and at least one device to remove");
- struct bcache_handle fs = bcache_fs_open(argv[optind]);
+ disk_ioctl(argv[optind], argv[optind + 1],
+ BCH_IOCTL_DISK_REMOVE, flags);
+ return 0;
+}
+
+static void device_online_usage(void)
+{
+ puts("bcache device online - readd a device to a running filesystem\n"
+ "Usage: bcache 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>");
+}
- for (unsigned i = optind + 1; i < argc; i++) {
- struct bch_ioctl_disk_set_state ir = {
- .dev = (__u64) argv[i],
- .new_state = BCH_MEMBER_STATE_FAILED,
- };
+int cmd_device_online(int argc, char *argv[])
+{
+ int opt;
- if (force_degraded)
- ir.flags |= BCH_FORCE_IF_DEGRADED;
- if (force_data)
- ir.flags |= BCH_FORCE_IF_DATA_LOST;
- if (force_metadata)
- ir.flags |= BCH_FORCE_IF_METADATA_LOST;
+ while ((opt = getopt(argc, argv, "h")) != -1)
+ switch (opt) {
+ case 'h':
+ device_online_usage();
+ exit(EXIT_SUCCESS);
+ }
- xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_SET_STATE, &ir);
- }
+ 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_remove_usage(void)
+static void device_offline_usage(void)
{
- puts("bcache device_remove - remove one or more devices from a filesystem\n"
- "Usage: bcache device_remove filesystem [devices]\n"
+ puts("bcache device offline - take a device offline, without removing it\n"
+ "Usage: bcache device offline [OPTION]... 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"
+ " -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("bcache device evacuate - move data off of a given device\n"
+ "Usage: bcache 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("bcache device set-state\n"
+ "Usage: bcache 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_remove(int argc, char *argv[])
+int cmd_device_set_state(int argc, char *argv[])
{
static const struct option longopts[] = {
- { "force", 0, NULL, 'f' },
- { "force-metadata", 0, NULL, 'F' },
- { "help", 0, NULL, 'h' },
+ { "force", 0, NULL, 'f' },
+ { "help", 0, NULL, 'h' },
{ NULL }
};
- int opt, force_data = 0, force_metadata = 0;
+ int opt, flags = 0;
while ((opt = getopt_long(argc, argv, "fh", longopts, NULL)) != -1)
switch (opt) {
case 'f':
- force_data = 1;
- break;
- case 'F':
- force_metadata = 1;
+ flags |= BCH_FORCE_IF_DEGRADED;
break;
case 'h':
- device_remove_usage();
+ device_set_state_usage();
}
- if (argc - optind < 2)
- die("Please supply a filesystem and at least one device to remove");
+ if (argc - optind != 3)
+ die("Please supply a filesystem, device and state");
struct bcache_handle fs = bcache_fs_open(argv[optind]);
- for (unsigned i = optind + 1; i < argc; i++) {
- struct bch_ioctl_disk_remove ir = {
- .dev = (__u64) argv[i],
- };
-
- if (force_data)
- ir.flags |= BCH_FORCE_IF_DATA_LOST;
- if (force_metadata)
- ir.flags |= BCH_FORCE_IF_METADATA_LOST;
-
- xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_REMOVE, &ir);
- }
+ struct bch_ioctl_disk_set_state i = {
+ .flags = flags,
+ .dev = (__u64) argv[optind + 1],
+ .new_state = read_string_list_or_die(argv[optind + 2],
+ bch_dev_state, "device state"),
+ };
+ xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_SET_STATE, &i);
return 0;
}
return 0;
}
+
+int cmd_show_super(int argc, char *argv[])
+{
+ struct bch_sb *sb;
+
+ if (argc != 2)
+ die("please supply a single device");
+
+ sb = bcache_super_read(argv[1]);
+ bcache_super_print(sb, HUMAN_READABLE);
+ return 0;
+}
#include "tools-util.h"
int cmd_format(int argc, char *argv[]);
+int cmd_show_super(int argc, char *argv[]);
int cmd_unlock(int argc, char *argv[]);
int cmd_assemble(int argc, char *argv[]);
int cmd_fs_show(int argc, char *argv[]);
int cmd_fs_set(int argc, char *argv[]);
-int cmd_device_show(int argc, char *argv[]);
int cmd_device_add(int argc, char *argv[]);
-int cmd_device_fail(int argc, char *argv[]);
int cmd_device_remove(int argc, char *argv[]);
+int cmd_device_online(int argc, char *argv[]);
+int cmd_device_offline(int argc, char *argv[]);
+int cmd_device_evacuate(int argc, char *argv[]);
+int cmd_device_set_state(int argc, char *argv[]);
int cmd_fsck(int argc, char *argv[]);
extern "C" {
#endif
-/* global control dev: */
-
#define BCH_FORCE_IF_DATA_LOST (1 << 0)
#define BCH_FORCE_IF_METADATA_LOST (1 << 1)
#define BCH_FORCE_IF_DATA_DEGRADED (1 << 2)
(BCH_FORCE_IF_DATA_DEGRADED| \
BCH_FORCE_IF_METADATA_DEGRADED)
-#define BCH_IOCTL_ASSEMBLE _IOW('r', 1, struct bch_ioctl_assemble)
-#define BCH_IOCTL_INCREMENTAL _IOW('r', 1, struct bch_ioctl_incremental)
-
-/* cache set control dev: */
+#define BCH_BY_UUID (1 << 4)
-#define BCH_IOCTL_RUN _IO('r', 2)
-#define BCH_IOCTL_STOP _IO('r', 3)
-
-#define BCH_IOCTL_DISK_ADD _IOW('r', 4, struct bch_ioctl_disk_add)
-#define BCH_IOCTL_DISK_REMOVE _IOW('r', 5, struct bch_ioctl_disk_remove)
-#define BCH_IOCTL_DISK_SET_STATE _IOW('r', 6, struct bch_ioctl_disk_set_state)
-
-#define BCH_IOCTL_DISK_REMOVE_BY_UUID \
- _IOW('r', 5, struct bch_ioctl_disk_remove_by_uuid)
-#define BCH_IOCTL_DISK_FAIL_BY_UUID \
- _IOW('r', 6, struct bch_ioctl_disk_fail_by_uuid)
+/* global control dev: */
-#define BCH_IOCTL_QUERY_UUID _IOR('r', 6, struct bch_ioctl_query_uuid)
+#define BCH_IOCTL_ASSEMBLE _IOW(0xbc, 1, struct bch_ioctl_assemble)
+#define BCH_IOCTL_INCREMENTAL _IOW(0xbc, 2, struct bch_ioctl_incremental)
struct bch_ioctl_assemble {
__u32 flags;
__u64 dev;
};
-struct bch_ioctl_disk_add {
+/* filesystem ioctls: */
+
+#define BCH_IOCTL_QUERY_UUID _IOR(0xbc, 1, struct bch_ioctl_query_uuid)
+#define BCH_IOCTL_START _IOW(0xbc, 2, struct bch_ioctl_start)
+#define BCH_IOCTL_STOP _IO(0xbc, 3)
+#define BCH_IOCTL_DISK_ADD _IOW(0xbc, 4, struct bch_ioctl_disk)
+#define BCH_IOCTL_DISK_REMOVE _IOW(0xbc, 5, struct bch_ioctl_disk)
+#define BCH_IOCTL_DISK_ONLINE _IOW(0xbc, 6, struct bch_ioctl_disk)
+#define BCH_IOCTL_DISK_OFFLINE _IOW(0xbc, 7, struct bch_ioctl_disk)
+#define BCH_IOCTL_DISK_SET_STATE _IOW(0xbc, 8, struct bch_ioctl_disk_set_state)
+#define BCH_IOCTL_DISK_EVACUATE _IOW(0xbc, 9, struct bch_ioctl_disk)
+#define BCH_IOCTL_DATA _IOW(0xbc, 10, struct bch_ioctl_data)
+
+struct bch_ioctl_query_uuid {
+ uuid_le uuid;
+};
+
+struct bch_ioctl_start {
__u32 flags;
__u32 pad;
- __u64 dev;
};
-struct bch_ioctl_disk_remove {
+struct bch_ioctl_disk {
__u32 flags;
__u32 pad;
__u64 dev;
__u64 dev;
};
-struct bch_ioctl_disk_remove_by_uuid {
- __u32 flags;
- __u32 pad;
- uuid_le dev;
-};
+#define BCH_REWRITE_INCREASE_REPLICAS (1 << 0)
+#define BCH_REWRITE_DECREASE_REPLICAS (1 << 1)
-struct bch_ioctl_disk_fail_by_uuid {
+#define BCH_REWRITE_RECOMPRESS (1 << 0)
+#define BCH_REWRITE_DECREASE_REPLICAS (1 << 1)
+
+struct bch_ioctl_data {
__u32 flags;
__u32 pad;
- uuid_le dev;
-};
-struct bch_ioctl_query_uuid {
- uuid_le uuid;
+ __u64 start_inode;
+ __u64 start_offset;
+
+ __u64 end_inode;
+ __u64 end_offset;
};
#ifdef __cplusplus
s64 fragmented = ((stats.buckets_dirty +
stats.buckets_cached) <<
bucket_bits) -
- ((stats.sectors_dirty +
- stats.sectors_cached) << 9);
+ ((stats.sectors[S_DIRTY] +
+ stats.sectors[S_CACHED] ) << 9);
- if (fragmented < 0)
- fragmented = 0;
+ fragmented = max(0LL, fragmented);
bch_pd_controller_update(&ca->moving_gc_pd,
free, fragmented, -1);
bch_btree_node_free_index(c, NULL, old->btree_id,
bkey_i_to_s_c(&old->key),
&stats);
- bch_fs_stats_apply(c, &stats, &btree_reserve->disk_res,
+ bch_fs_usage_apply(c, &stats, &btree_reserve->disk_res,
gc_pos_btree_root(b->btree_id));
}
bkey_disassemble(b, k, &tmp),
&stats);
- bch_fs_stats_apply(c, &stats, disk_res, gc_pos_btree_node(b));
+ bch_fs_usage_apply(c, &stats, disk_res, gc_pos_btree_node(b));
bch_btree_bset_insert_key(iter, b, node_iter, insert);
set_btree_node_dirty(b);
!m.dirty_sectors && !!m.cached_sectors;
}
-void bch_fs_stats_apply(struct bch_fs *c,
+static inline enum s_alloc bucket_type(struct bucket_mark m)
+{
+ return is_meta_bucket(m) ? S_META : S_DIRTY;
+}
+
+static bool bucket_became_unavailable(struct bch_fs *c,
+ struct bucket_mark old,
+ struct bucket_mark new)
+{
+ return is_available_bucket(old) &&
+ !is_available_bucket(new) &&
+ c && c->gc_pos.phase == GC_PHASE_DONE;
+}
+
+void bch_fs_usage_apply(struct bch_fs *c,
struct bch_fs_usage *stats,
struct disk_reservation *disk_res,
struct gc_pos gc_pos)
memset(stats, 0, sizeof(*stats));
}
-static bool bucket_became_unavailable(struct bch_fs *c,
- struct bucket_mark old,
- struct bucket_mark new)
+static void bch_fs_usage_update(struct bch_fs_usage *fs_usage,
+ struct bucket_mark old, struct bucket_mark new)
{
- return is_available_bucket(old) &&
- !is_available_bucket(new) &&
- c && c->gc_pos.phase == GC_PHASE_DONE;
+ fs_usage->s[S_COMPRESSED][S_CACHED] +=
+ (int) new.cached_sectors - (int) old.cached_sectors;
+ fs_usage->s[S_COMPRESSED][bucket_type(old)] -=
+ old.dirty_sectors;
+ fs_usage->s[S_COMPRESSED][bucket_type(new)] +=
+ new.dirty_sectors;
}
-static void bch_usage_update(struct bch_dev *ca,
- struct bucket_mark old, struct bucket_mark new,
- struct bch_fs_usage *bch_alloc_stats)
+static void bch_dev_usage_update(struct bch_dev *ca,
+ struct bucket_mark old, struct bucket_mark new)
{
struct bch_fs *c = ca->fs;
- struct bch_dev_usage *cache_stats;
+ struct bch_dev_usage *dev_usage;
bch_fs_inconsistent_on(old.data_type && new.data_type &&
old.data_type != new.data_type, c,
"different types of metadata in same bucket: %u, %u",
old.data_type, new.data_type);
- if (bch_alloc_stats) {
- bch_alloc_stats->s[S_COMPRESSED][S_CACHED] +=
- (int) new.cached_sectors - (int) old.cached_sectors;
-
- bch_alloc_stats->s[S_COMPRESSED]
- [is_meta_bucket(old) ? S_META : S_DIRTY] -=
- old.dirty_sectors;
-
- bch_alloc_stats->s[S_COMPRESSED]
- [is_meta_bucket(new) ? S_META : S_DIRTY] +=
- new.dirty_sectors;
- }
-
preempt_disable();
- cache_stats = this_cpu_ptr(ca->usage_percpu);
+ dev_usage = this_cpu_ptr(ca->usage_percpu);
- cache_stats->sectors_cached +=
+ dev_usage->sectors[S_CACHED] +=
(int) new.cached_sectors - (int) old.cached_sectors;
- if (is_meta_bucket(old))
- cache_stats->sectors_meta -= old.dirty_sectors;
- else
- cache_stats->sectors_dirty -= old.dirty_sectors;
+ dev_usage->sectors[bucket_type(old)] -= old.dirty_sectors;
+ dev_usage->sectors[bucket_type(new)] += new.dirty_sectors;
- if (is_meta_bucket(new))
- cache_stats->sectors_meta += new.dirty_sectors;
- else
- cache_stats->sectors_dirty += new.dirty_sectors;
-
- cache_stats->buckets_alloc +=
+ dev_usage->buckets_alloc +=
(int) new.owned_by_allocator - (int) old.owned_by_allocator;
- cache_stats->buckets_meta += is_meta_bucket(new) - is_meta_bucket(old);
- cache_stats->buckets_cached += is_cached_bucket(new) - is_cached_bucket(old);
- cache_stats->buckets_dirty += is_dirty_bucket(new) - is_dirty_bucket(old);
+ dev_usage->buckets_meta += is_meta_bucket(new) - is_meta_bucket(old);
+ dev_usage->buckets_cached += is_cached_bucket(new) - is_cached_bucket(old);
+ dev_usage->buckets_dirty += is_dirty_bucket(new) - is_dirty_bucket(old);
preempt_enable();
if (!is_available_bucket(old) && is_available_bucket(new))
#define bucket_data_cmpxchg(ca, g, new, expr) \
({ \
- struct bch_fs_usage _stats = { 0 }; \
struct bucket_mark _old = bucket_cmpxchg(g, new, expr); \
\
- bch_usage_update(ca, _old, new, &_stats); \
+ bch_dev_usage_update(ca, _old, new); \
_old; \
})
struct bch_fs_usage stats = { 0 };
struct bucket_mark old, new;
- old = bucket_cmpxchg(g, new, ({
+ old = bucket_data_cmpxchg(ca, g, new, ({
new.owned_by_allocator = 1;
new.had_metadata = 0;
new.data_type = 0;
new.gen++;
}));
- bch_usage_update(ca, old, new, &stats);
-
- BUG_ON(old.dirty_sectors);
-
- /*
- * Ick:
- *
- * Only stats.sectors_cached should be nonzero: this is important
- * because in this path we modify bch_alloc_stats based on how the
- * bucket_mark was modified, and the sector counts in bucket_mark are
- * subject to (saturating) overflow - and if they did overflow, the
- * bch_fs_usage stats will now be off. We can tolerate this for
- * sectors_cached, but not anything else:
- */
- stats.s[S_COMPRESSED][S_CACHED] = 0;
- stats.s[S_UNCOMPRESSED][S_CACHED] = 0;
- BUG_ON(!bch_is_zero(&stats, sizeof(stats)));
+ /* XXX: we're not actually updating fs usage's cached sectors... */
+ bch_fs_usage_update(&stats, old, new);
if (!old.owned_by_allocator && old.cached_sectors)
trace_bcache_invalidate(ca, g - ca->buckets,
unsigned saturated;
struct bch_dev *ca = c->devs[ptr->dev];
struct bucket *g = ca->buckets + PTR_BUCKET_NR(ca, ptr);
- u64 v;
unsigned old_sectors, new_sectors;
int disk_sectors, compressed_sectors;
goto out;
}
- v = READ_ONCE(g->_mark.counter);
- do {
- new.counter = old.counter = v;
+ old = bucket_data_cmpxchg(ca, g, new, ({
saturated = 0;
/*
}
new.had_metadata |= is_meta_bucket(new);
- } while ((v = cmpxchg(&g->_mark.counter,
- old.counter,
- new.counter)) != old.counter);
-
- bch_usage_update(ca, old, new, NULL);
+ }));
BUG_ON(!may_make_unavailable &&
bucket_became_unavailable(c, old, new));
struct bch_fs_usage __bch_fs_usage_read(struct bch_fs *);
struct bch_fs_usage bch_fs_usage_read(struct bch_fs *);
-void bch_fs_stats_apply(struct bch_fs *, struct bch_fs_usage *,
+void bch_fs_usage_apply(struct bch_fs *, struct bch_fs_usage *,
struct disk_reservation *, struct gc_pos);
static inline u64 __bch_fs_sectors_used(struct bch_fs *c)
};
};
-struct bch_dev_usage {
- u64 buckets_dirty;
- u64 buckets_cached;
- u64 buckets_meta;
- u64 buckets_alloc;
-
- u64 sectors_dirty;
- u64 sectors_cached;
- u64 sectors_meta;
+enum s_compressed {
+ S_COMPRESSED,
+ S_UNCOMPRESSED,
+ S_COMPRESSED_NR,
};
enum s_alloc {
S_ALLOC_NR,
};
-enum s_compressed {
- S_COMPRESSED,
- S_UNCOMPRESSED,
- S_COMPRESSED_NR,
+struct bch_dev_usage {
+ u64 buckets_dirty;
+ u64 buckets_cached;
+ u64 buckets_meta;
+ u64 buckets_alloc;
+
+ u64 sectors[S_ALLOC_NR];
};
struct bch_fs_usage {
if (copy_from_user(&arg, user_arg, sizeof(arg)))
return -EFAULT;
+ if (arg.flags || arg.pad)
+ return -EINVAL;
+
user_devs = kmalloc_array(arg.nr_devs, sizeof(u64), GFP_KERNEL);
if (!devs)
return -ENOMEM;
if (copy_from_user(&arg, user_arg, sizeof(arg)))
return -EFAULT;
+ if (arg.flags || arg.pad)
+ return -EINVAL;
+
path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
if (!path)
return -ENOMEM;
}
}
-static long bch_ioctl_stop(struct bch_fs *c)
+static long bch_ioctl_query_uuid(struct bch_fs *c,
+ struct bch_ioctl_query_uuid __user *user_arg)
{
- bch_fs_stop_async(c);
- return 0;
+ return copy_to_user(&user_arg->uuid,
+ &c->sb.user_uuid,
+ sizeof(c->sb.user_uuid));
}
-static long bch_ioctl_disk_add(struct bch_fs *c,
- struct bch_ioctl_disk_add __user *user_arg)
+static long bch_ioctl_start(struct bch_fs *c, struct bch_ioctl_start __user *user_arg)
{
- struct bch_ioctl_disk_add arg;
- char *path;
- int ret;
+ struct bch_ioctl_start arg;
if (copy_from_user(&arg, user_arg, sizeof(arg)))
return -EFAULT;
- path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
- if (!path)
- return -ENOMEM;
+ if (arg.flags || arg.pad)
+ return -EINVAL;
- ret = bch_dev_add(c, path);
- kfree(path);
+ return bch_fs_start(c) ? -EIO : 0;
+}
- return ret;
+static long bch_ioctl_stop(struct bch_fs *c)
+{
+ bch_fs_stop(c);
+ return 0;
}
/* returns with ref on ca->ref */
return ca;
}
-static long bch_ioctl_disk_remove(struct bch_fs *c,
- struct bch_ioctl_disk_remove __user *user_arg)
+#if 0
+static struct bch_member *bch_uuid_lookup(struct bch_fs *c, uuid_le uuid)
{
- struct bch_ioctl_disk_remove arg;
- struct bch_dev *ca;
+ struct bch_sb_field_members *mi = bch_sb_get_members(c->disk_sb);
+ unsigned i;
+
+ lockdep_assert_held(&c->sb_lock);
+
+ for (i = 0; i < c->disk_sb->nr_devices; i++)
+ if (!memcmp(&mi->members[i].uuid, &uuid, sizeof(uuid)))
+ return &mi->members[i];
+
+ return NULL;
+}
+#endif
+
+static long bch_ioctl_disk_add(struct bch_fs *c,
+ struct bch_ioctl_disk __user *user_arg)
+{
+ struct bch_ioctl_disk arg;
+ char *path;
int ret;
if (copy_from_user(&arg, user_arg, sizeof(arg)))
return -EFAULT;
- ca = bch_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
- if (IS_ERR(ca))
- return PTR_ERR(ca);
+ if (arg.flags || arg.pad)
+ return -EINVAL;
- ret = bch_dev_remove(c, ca, arg.flags);
+ path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
+ if (!path)
+ return -ENOMEM;
+
+ ret = bch_dev_add(c, path);
+ kfree(path);
return ret;
}
-static long bch_ioctl_disk_set_state(struct bch_fs *c,
- struct bch_ioctl_disk_set_state __user *user_arg)
+static long bch_ioctl_disk_remove(struct bch_fs *c,
+ struct bch_ioctl_disk __user *user_arg)
{
- struct bch_ioctl_disk_set_state arg;
+ struct bch_ioctl_disk arg;
struct bch_dev *ca;
int ret;
if (IS_ERR(ca))
return PTR_ERR(ca);
- ret = bch_dev_set_state(c, ca, arg.new_state, arg.flags);
+ ret = bch_dev_remove(c, ca, arg.flags);
percpu_ref_put(&ca->ref);
return ret;
}
-static struct bch_member *bch_uuid_lookup(struct bch_fs *c, uuid_le uuid)
+static long bch_ioctl_disk_online(struct bch_fs *c,
+ struct bch_ioctl_disk __user *user_arg)
{
- struct bch_sb_field_members *mi = bch_sb_get_members(c->disk_sb);
- unsigned i;
+ struct bch_ioctl_disk arg;
+ char *path;
+ int ret;
- lockdep_assert_held(&c->sb_lock);
+ if (copy_from_user(&arg, user_arg, sizeof(arg)))
+ return -EFAULT;
- for (i = 0; i < c->disk_sb->nr_devices; i++)
- if (!memcmp(&mi->members[i].uuid, &uuid, sizeof(uuid)))
- return &mi->members[i];
+ if (arg.flags || arg.pad)
+ return -EINVAL;
- return NULL;
+ path = strndup_user((const char __user *)(unsigned long) arg.dev, PATH_MAX);
+ if (!path)
+ return -ENOMEM;
+
+ ret = bch_dev_online(c, path);
+ kfree(path);
+ return ret;
}
-static long bch_ioctl_disk_remove_by_uuid(struct bch_fs *c,
- struct bch_ioctl_disk_remove_by_uuid __user *user_arg)
+static long bch_ioctl_disk_offline(struct bch_fs *c,
+ struct bch_ioctl_disk __user *user_arg)
{
- struct bch_ioctl_disk_fail_by_uuid arg;
- struct bch_member *m;
- int ret = -ENOENT;
+ struct bch_ioctl_disk arg;
+ struct bch_dev *ca;
+ int ret;
if (copy_from_user(&arg, user_arg, sizeof(arg)))
return -EFAULT;
- mutex_lock(&c->sb_lock);
- if ((m = bch_uuid_lookup(c, arg.dev))) {
- /* XXX: */
- SET_BCH_MEMBER_STATE(m, BCH_MEMBER_STATE_FAILED);
- bch_write_super(c);
- ret = 0;
- }
- mutex_unlock(&c->sb_lock);
+ if (arg.pad)
+ return -EINVAL;
+ ca = bch_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
+ if (IS_ERR(ca))
+ return PTR_ERR(ca);
+
+ ret = bch_dev_offline(c, ca, arg.flags);
+ percpu_ref_put(&ca->ref);
return ret;
}
-static long bch_ioctl_disk_fail_by_uuid(struct bch_fs *c,
- struct bch_ioctl_disk_fail_by_uuid __user *user_arg)
+static long bch_ioctl_disk_set_state(struct bch_fs *c,
+ struct bch_ioctl_disk_set_state __user *user_arg)
{
- struct bch_ioctl_disk_fail_by_uuid arg;
- struct bch_member *m;
- int ret = -ENOENT;
+ struct bch_ioctl_disk_set_state arg;
+ struct bch_dev *ca;
+ int ret;
if (copy_from_user(&arg, user_arg, sizeof(arg)))
return -EFAULT;
- mutex_lock(&c->sb_lock);
- if ((m = bch_uuid_lookup(c, arg.dev))) {
- SET_BCH_MEMBER_STATE(m, BCH_MEMBER_STATE_FAILED);
- bch_write_super(c);
- ret = 0;
- }
- mutex_unlock(&c->sb_lock);
+ ca = bch_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
+ if (IS_ERR(ca))
+ return PTR_ERR(ca);
+ ret = bch_dev_set_state(c, ca, arg.new_state, arg.flags);
+
+ percpu_ref_put(&ca->ref);
return ret;
}
-static long bch_ioctl_query_uuid(struct bch_fs *c,
- struct bch_ioctl_query_uuid __user *user_arg)
+static long bch_ioctl_disk_evacuate(struct bch_fs *c,
+ struct bch_ioctl_disk __user *user_arg)
{
- return copy_to_user(&user_arg->uuid,
- &c->sb.user_uuid,
- sizeof(c->sb.user_uuid));
+ struct bch_ioctl_disk arg;
+ struct bch_dev *ca;
+ int ret;
+
+ if (copy_from_user(&arg, user_arg, sizeof(arg)))
+ return -EFAULT;
+
+ ca = bch_device_lookup(c, (const char __user *)(unsigned long) arg.dev);
+ if (IS_ERR(ca))
+ return PTR_ERR(ca);
+
+ ret = bch_dev_migrate(c, ca);
+
+ percpu_ref_put(&ca->ref);
+ return ret;
}
long bch_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
/* ioctls that do require admin cap: */
switch (cmd) {
- case BCH_IOCTL_RUN:
- return -ENOTTY;
+ case BCH_IOCTL_START:
+ return bch_ioctl_start(c, arg);
case BCH_IOCTL_STOP:
return bch_ioctl_stop(c);
return bch_ioctl_disk_add(c, arg);
case BCH_IOCTL_DISK_REMOVE:
return bch_ioctl_disk_remove(c, arg);
+ case BCH_IOCTL_DISK_ONLINE:
+ return bch_ioctl_disk_online(c, arg);
+ case BCH_IOCTL_DISK_OFFLINE:
+ return bch_ioctl_disk_offline(c, arg);
case BCH_IOCTL_DISK_SET_STATE:
return bch_ioctl_disk_set_state(c, arg);
-
- case BCH_IOCTL_DISK_REMOVE_BY_UUID:
- return bch_ioctl_disk_remove_by_uuid(c, arg);
- case BCH_IOCTL_DISK_FAIL_BY_UUID:
- return bch_ioctl_disk_fail_by_uuid(c, arg);
+ case BCH_IOCTL_DISK_EVACUATE:
+ return bch_ioctl_disk_evacuate(c, arg);
default:
return -ENOTTY;
struct btree_insert *trans;
struct btree_insert_entry *insert;
struct bpos committed;
- struct bch_fs_usage stats;
+ struct bch_fs_usage stats;
/* for deleting: */
struct bkey_i whiteout;
stop:
extent_insert_committed(s);
- bch_fs_stats_apply(c, &s->stats, s->trans->disk_res,
+ bch_fs_usage_apply(c, &s->stats, s->trans->disk_res,
gc_pos_btree_node(b));
EBUG_ON(bkey_cmp(iter->pos, s->committed));
bkey_start_offset(&insert->k->k),
insert->k->k.size);
- bch_fs_stats_apply(c, &s.stats, trans->disk_res,
+ bch_fs_usage_apply(c, &s.stats, trans->disk_res,
gc_pos_btree_node(b));
EBUG_ON(bkey_cmp(iter->pos, bkey_start_pos(&insert->k->k)));
notify_put(c);
}
-void bch_notify_dev_removing(struct bch_dev *ca)
-{
- struct bch_fs *c = ca->fs;
-
- notify_get_cache(ca);
- notify_var(c, "STATE=removing");
- notify_put(c);
-}
-
-void bch_notify_dev_remove_failed(struct bch_dev *ca)
-{
- struct bch_fs *c = ca->fs;
-
- notify_get_cache(ca);
- notify_var(c, "STATE=remove_failed");
- notify_put(c);
-}
-
-void bch_notify_dev_removed(struct bch_dev *ca)
-{
- struct bch_fs *c = ca->fs;
-
- notify_get_cache(ca);
- notify_var(c, "STATE=removed");
- notify_put(c);
-}
-
void bch_notify_dev_error(struct bch_dev *ca, bool fatal)
{
struct bch_fs *c = ca->fs;
complete(&ca->offline_complete);
}
-static void bch_dev_offline(struct bch_dev *ca)
+static void __bch_dev_offline(struct bch_dev *ca)
{
struct bch_fs *c = ca->fs;
return -ENOMEM;
}
-static int bch_dev_online(struct bch_fs *c, struct bcache_superblock *sb)
+static int __bch_dev_online(struct bch_fs *c, struct bcache_superblock *sb)
{
struct bch_dev *ca;
int ret;
bch_journal_meta(&c->journal);
- bch_dev_offline(ca);
+ __bch_dev_offline(ca);
bch_dev_stop(ca);
bch_dev_free(ca);
goto err_unlock;
}
- if (bch_dev_online(c, &sb)) {
+ if (__bch_dev_online(c, &sb)) {
err = "bch_dev_online() error";
ret = -ENOMEM;
goto err_unlock;
return ret ?: -EINVAL;
}
+int bch_dev_online(struct bch_fs *c, const char *path)
+{
+ struct bcache_superblock sb = { 0 };
+ const char *err;
+
+ mutex_lock(&c->state_lock);
+
+ err = bch_read_super(&sb, bch_opts_empty(), path);
+ if (err)
+ goto err;
+
+ err = bch_dev_in_fs(c->disk_sb, sb.sb);
+ if (err)
+ goto err;
+
+ mutex_lock(&c->sb_lock);
+ if (__bch_dev_online(c, &sb)) {
+ mutex_unlock(&c->sb_lock);
+ goto err;
+ }
+ mutex_unlock(&c->sb_lock);
+
+ mutex_unlock(&c->state_lock);
+ return 0;
+err:
+ mutex_unlock(&c->state_lock);
+ bch_free_super(&sb);
+ bch_err(c, "error bringing %s online: %s", path, err);
+ return -EINVAL;
+}
+
+int bch_dev_offline(struct bch_fs *c, struct bch_dev *ca, int flags)
+{
+ mutex_lock(&c->state_lock);
+
+ if (!bch_dev_state_allowed(c, ca, BCH_MEMBER_STATE_FAILED, flags)) {
+ bch_err(ca, "Cannot offline required disk");
+ mutex_unlock(&c->state_lock);
+ return -EINVAL;
+ }
+
+ __bch_dev_read_only(c, ca);
+ __bch_dev_offline(ca);
+
+ mutex_unlock(&c->state_lock);
+ return 0;
+}
+
+int bch_dev_migrate(struct bch_fs *c, struct bch_dev *ca)
+{
+ int ret;
+
+ mutex_lock(&c->state_lock);
+
+ if (ca->mi.state == BCH_MEMBER_STATE_RW) {
+ bch_err(ca, "Cannot migrate data off RW device");
+ mutex_unlock(&c->state_lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&c->state_lock);
+
+ ret = bch_move_data_off_device(ca);
+ if (ret) {
+ bch_err(ca, "Error migrating data: %i", ret);
+ return ret;
+ }
+
+ ret = bch_move_metadata_off_device(ca);
+ if (ret) {
+ bch_err(ca, "Error migrating metadata: %i", ret);
+ return ret;
+ }
+
+ if (ca->mi.has_data || ca->mi.has_metadata) {
+ bch_err(ca, "Migrate error: data still present");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/* Filesystem open: */
const char *bch_fs_open(char * const *devices, unsigned nr_devices,
err = "bch_dev_online() error";
mutex_lock(&c->sb_lock);
for (i = 0; i < nr_devices; i++)
- if (bch_dev_online(c, &sb[i])) {
+ if (__bch_dev_online(c, &sb[i])) {
mutex_unlock(&c->sb_lock);
goto err;
}
err = "bch_dev_online() error";
mutex_lock(&c->sb_lock);
- if (bch_dev_online(c, sb)) {
+ if (__bch_dev_online(c, sb)) {
mutex_unlock(&c->sb_lock);
goto err;
}
int bch_dev_fail(struct bch_dev *, int);
int bch_dev_remove(struct bch_fs *, struct bch_dev *, int);
int bch_dev_add(struct bch_fs *, const char *);
+int bch_dev_online(struct bch_fs *, const char *);
+int bch_dev_offline(struct bch_fs *, struct bch_dev *, int);
+int bch_dev_migrate(struct bch_fs *, struct bch_dev *);
void bch_fs_detach(struct bch_fs *);
sysfs_print(io_errors,
atomic_read(&ca->io_errors) >> IO_ERROR_SHIFT);
- sysfs_hprint(dirty_data, stats.sectors_dirty << 9);
- sysfs_print(dirty_bytes, stats.sectors_dirty << 9);
+ sysfs_hprint(dirty_data, stats.sectors[S_DIRTY] << 9);
+ sysfs_print(dirty_bytes, stats.sectors[S_DIRTY] << 9);
sysfs_print(dirty_buckets, stats.buckets_dirty);
- sysfs_hprint(cached_data, stats.sectors_cached << 9);
- sysfs_print(cached_bytes, stats.sectors_cached << 9);
+ sysfs_hprint(cached_data, stats.sectors[S_CACHED] << 9);
+ sysfs_print(cached_bytes, stats.sectors[S_CACHED] << 9);
sysfs_print(cached_buckets, stats.buckets_cached);
sysfs_print(meta_buckets, stats.buckets_meta);
sysfs_print(alloc_buckets, stats.buckets_alloc);