Allow disk commands to specify member by index

This commit is contained in:
Kent Overstreet 2017-04-05 17:31:05 -08:00
parent bc85a94114
commit 2615d73a74
7 changed files with 134 additions and 128 deletions

View File

@ -1 +1 @@
83667254ddf04f558c90f32439e36d7a04ac3a39 297c81ae4d608707fdabedc60158ff1f4fbec257

View File

@ -167,7 +167,12 @@ int cmd_device_show(int argc, char *argv[])
static void disk_ioctl(const char *fs, const char *dev, int cmd, int flags) static void disk_ioctl(const char *fs, const char *dev, int cmd, int flags)
{ {
struct bch_ioctl_disk i = { .flags = flags, .dev = (__u64) dev, }; 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); xioctl(bcache_fs_open(fs).ioctl_fd, cmd, &i);
} }
@ -435,11 +440,16 @@ int cmd_device_set_state(int argc, char *argv[])
struct bch_ioctl_disk_set_state i = { struct bch_ioctl_disk_set_state i = {
.flags = flags, .flags = flags,
.dev = (__u64) argv[optind + 1],
.new_state = read_string_list_or_die(argv[optind + 2], .new_state = read_string_list_or_die(argv[optind + 2],
bch2_dev_state, "device state"), bch2_dev_state, "device state"),
}; };
const char *dev = argv[optind + 1];
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); xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_SET_STATE, &i);
return 0; return 0;
} }

View File

@ -17,7 +17,7 @@ extern "C" {
(BCH_FORCE_IF_DATA_DEGRADED| \ (BCH_FORCE_IF_DATA_DEGRADED| \
BCH_FORCE_IF_METADATA_DEGRADED) BCH_FORCE_IF_METADATA_DEGRADED)
#define BCH_BY_UUID (1 << 4) #define BCH_BY_INDEX (1 << 4)
/* global control dev: */ /* global control dev: */

View File

@ -1288,6 +1288,9 @@ static void btree_node_write_endio(struct bio *bio)
bch2_meta_write_fault("btree")) bch2_meta_write_fault("btree"))
set_btree_node_write_error(b); set_btree_node_write_error(b);
if (wbio->have_io_ref)
percpu_ref_put(&ca->io_ref);
if (wbio->bounce) if (wbio->bounce)
btree_bounce_free(c, btree_bounce_free(c,
wbio->order, wbio->order,
@ -1304,9 +1307,6 @@ static void btree_node_write_endio(struct bio *bio)
if (cl) if (cl)
closure_put(cl); closure_put(cl);
} }
if (wbio->have_io_ref)
percpu_ref_put(&ca->io_ref);
} }
void __bch2_btree_node_write(struct bch_fs *c, struct btree *b, void __bch2_btree_node_write(struct bch_fs *c, struct btree *b,

View File

@ -12,6 +12,51 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/slab.h> #include <linux/slab.h>
/* returns with ref on ca->ref */
static struct bch_dev *bch2_device_lookup(struct bch_fs *c, u64 dev,
unsigned flags)
{
struct bch_dev *ca;
if (flags & BCH_BY_INDEX) {
if (dev >= c->sb.nr_devices)
return ERR_PTR(-EINVAL);
rcu_read_lock();
ca = c->devs[dev];
if (ca)
percpu_ref_get(&ca->ref);
rcu_read_unlock();
if (!ca)
return ERR_PTR(-EINVAL);
} else {
struct block_device *bdev;
char *path;
unsigned i;
path = strndup_user((const char __user *)
(unsigned long) dev, PATH_MAX);
if (!path)
return ERR_PTR(-ENOMEM);
bdev = lookup_bdev(strim(path));
kfree(path);
if (IS_ERR(bdev))
return ERR_CAST(bdev);
for_each_member_device(ca, c, i)
if (ca->disk_sb.bdev == bdev)
goto found;
ca = NULL;
found:
bdput(bdev);
}
return ca;
}
static long bch2_ioctl_assemble(struct bch_ioctl_assemble __user *user_arg) static long bch2_ioctl_assemble(struct bch_ioctl_assemble __user *user_arg)
{ {
struct bch_ioctl_assemble arg; struct bch_ioctl_assemble arg;
@ -110,13 +155,8 @@ static long bch2_ioctl_query_uuid(struct bch_fs *c,
sizeof(c->sb.user_uuid)); sizeof(c->sb.user_uuid));
} }
static long bch2_ioctl_start(struct bch_fs *c, struct bch_ioctl_start __user *user_arg) static long bch2_ioctl_start(struct bch_fs *c, struct bch_ioctl_start arg)
{ {
struct bch_ioctl_start arg;
if (copy_from_user(&arg, user_arg, sizeof(arg)))
return -EFAULT;
if (arg.flags || arg.pad) if (arg.flags || arg.pad)
return -EINVAL; return -EINVAL;
@ -129,60 +169,11 @@ static long bch2_ioctl_stop(struct bch_fs *c)
return 0; return 0;
} }
/* returns with ref on ca->ref */ static long bch2_ioctl_disk_add(struct bch_fs *c, struct bch_ioctl_disk arg)
static struct bch_dev *bch2_device_lookup(struct bch_fs *c,
const char __user *dev)
{ {
struct block_device *bdev;
struct bch_dev *ca;
char *path;
unsigned i;
path = strndup_user(dev, PATH_MAX);
if (!path)
return ERR_PTR(-ENOMEM);
bdev = lookup_bdev(strim(path));
kfree(path);
if (IS_ERR(bdev))
return ERR_CAST(bdev);
for_each_member_device(ca, c, i)
if (ca->disk_sb.bdev == bdev)
goto found;
ca = NULL;
found:
bdput(bdev);
return ca;
}
#if 0
static struct bch_member *bch2_uuid_lookup(struct bch_fs *c, uuid_le uuid)
{
struct bch_sb_field_members *mi = bch2_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 bch2_ioctl_disk_add(struct bch_fs *c,
struct bch_ioctl_disk __user *user_arg)
{
struct bch_ioctl_disk arg;
char *path; char *path;
int ret; int ret;
if (copy_from_user(&arg, user_arg, sizeof(arg)))
return -EFAULT;
if (arg.flags || arg.pad) if (arg.flags || arg.pad)
return -EINVAL; return -EINVAL;
@ -196,32 +187,29 @@ static long bch2_ioctl_disk_add(struct bch_fs *c,
return ret; return ret;
} }
static long bch2_ioctl_disk_remove(struct bch_fs *c, static long bch2_ioctl_disk_remove(struct bch_fs *c, struct bch_ioctl_disk arg)
struct bch_ioctl_disk __user *user_arg)
{ {
struct bch_ioctl_disk arg;
struct bch_dev *ca; struct bch_dev *ca;
if (copy_from_user(&arg, user_arg, sizeof(arg))) if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
return -EFAULT; BCH_FORCE_IF_METADATA_LOST|
BCH_FORCE_IF_DEGRADED|
BCH_BY_INDEX)) ||
arg.pad)
return -EINVAL;
ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev); ca = bch2_device_lookup(c, arg.dev, arg.flags);
if (IS_ERR(ca)) if (IS_ERR(ca))
return PTR_ERR(ca); return PTR_ERR(ca);
return bch2_dev_remove(c, ca, arg.flags); return bch2_dev_remove(c, ca, arg.flags);
} }
static long bch2_ioctl_disk_online(struct bch_fs *c, static long bch2_ioctl_disk_online(struct bch_fs *c, struct bch_ioctl_disk arg)
struct bch_ioctl_disk __user *user_arg)
{ {
struct bch_ioctl_disk arg;
char *path; char *path;
int ret; int ret;
if (copy_from_user(&arg, user_arg, sizeof(arg)))
return -EFAULT;
if (arg.flags || arg.pad) if (arg.flags || arg.pad)
return -EINVAL; return -EINVAL;
@ -234,20 +222,19 @@ static long bch2_ioctl_disk_online(struct bch_fs *c,
return ret; return ret;
} }
static long bch2_ioctl_disk_offline(struct bch_fs *c, static long bch2_ioctl_disk_offline(struct bch_fs *c, struct bch_ioctl_disk arg)
struct bch_ioctl_disk __user *user_arg)
{ {
struct bch_ioctl_disk arg;
struct bch_dev *ca; struct bch_dev *ca;
int ret; int ret;
if (copy_from_user(&arg, user_arg, sizeof(arg))) if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
return -EFAULT; BCH_FORCE_IF_METADATA_LOST|
BCH_FORCE_IF_DEGRADED|
if (arg.pad) BCH_BY_INDEX)) ||
arg.pad)
return -EINVAL; return -EINVAL;
ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev); ca = bch2_device_lookup(c, arg.dev, arg.flags);
if (IS_ERR(ca)) if (IS_ERR(ca))
return PTR_ERR(ca); return PTR_ERR(ca);
@ -257,16 +244,19 @@ static long bch2_ioctl_disk_offline(struct bch_fs *c,
} }
static long bch2_ioctl_disk_set_state(struct bch_fs *c, static long bch2_ioctl_disk_set_state(struct bch_fs *c,
struct bch_ioctl_disk_set_state __user *user_arg) struct bch_ioctl_disk_set_state arg)
{ {
struct bch_ioctl_disk_set_state arg;
struct bch_dev *ca; struct bch_dev *ca;
int ret; int ret;
if (copy_from_user(&arg, user_arg, sizeof(arg))) if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
return -EFAULT; BCH_FORCE_IF_METADATA_LOST|
BCH_FORCE_IF_DEGRADED|
BCH_BY_INDEX)) ||
arg.pad[0] || arg.pad[1] || arg.pad[2])
return -EINVAL;
ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev); ca = bch2_device_lookup(c, arg.dev, arg.flags);
if (IS_ERR(ca)) if (IS_ERR(ca))
return PTR_ERR(ca); return PTR_ERR(ca);
@ -277,16 +267,16 @@ static long bch2_ioctl_disk_set_state(struct bch_fs *c,
} }
static long bch2_ioctl_disk_evacuate(struct bch_fs *c, static long bch2_ioctl_disk_evacuate(struct bch_fs *c,
struct bch_ioctl_disk __user *user_arg) struct bch_ioctl_disk arg)
{ {
struct bch_ioctl_disk arg;
struct bch_dev *ca; struct bch_dev *ca;
int ret; int ret;
if (copy_from_user(&arg, user_arg, sizeof(arg))) if ((arg.flags & ~BCH_BY_INDEX) ||
return -EFAULT; arg.pad)
return -EINVAL;
ca = bch2_device_lookup(c, (const char __user *)(unsigned long) arg.dev); ca = bch2_device_lookup(c, arg.dev, arg.flags);
if (IS_ERR(ca)) if (IS_ERR(ca))
return PTR_ERR(ca); return PTR_ERR(ca);
@ -296,6 +286,15 @@ static long bch2_ioctl_disk_evacuate(struct bch_fs *c,
return ret; return ret;
} }
#define BCH_IOCTL(_name, _argtype) \
do { \
_argtype i; \
\
if (copy_from_user(&i, arg, sizeof(i))) \
return -EFAULT; \
return bch2_ioctl_##_name(c, i); \
} while (0)
long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg) long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
{ {
/* ioctls that don't require admin cap: */ /* ioctls that don't require admin cap: */
@ -310,22 +309,22 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
/* ioctls that do require admin cap: */ /* ioctls that do require admin cap: */
switch (cmd) { switch (cmd) {
case BCH_IOCTL_START: case BCH_IOCTL_START:
return bch2_ioctl_start(c, arg); BCH_IOCTL(start, struct bch_ioctl_start);
case BCH_IOCTL_STOP: case BCH_IOCTL_STOP:
return bch2_ioctl_stop(c); return bch2_ioctl_stop(c);
case BCH_IOCTL_DISK_ADD: case BCH_IOCTL_DISK_ADD:
return bch2_ioctl_disk_add(c, arg); BCH_IOCTL(disk_add, struct bch_ioctl_disk);
case BCH_IOCTL_DISK_REMOVE: case BCH_IOCTL_DISK_REMOVE:
return bch2_ioctl_disk_remove(c, arg); BCH_IOCTL(disk_remove, struct bch_ioctl_disk);
case BCH_IOCTL_DISK_ONLINE: case BCH_IOCTL_DISK_ONLINE:
return bch2_ioctl_disk_online(c, arg); BCH_IOCTL(disk_online, struct bch_ioctl_disk);
case BCH_IOCTL_DISK_OFFLINE: case BCH_IOCTL_DISK_OFFLINE:
return bch2_ioctl_disk_offline(c, arg); BCH_IOCTL(disk_offline, struct bch_ioctl_disk);
case BCH_IOCTL_DISK_SET_STATE: case BCH_IOCTL_DISK_SET_STATE:
return bch2_ioctl_disk_set_state(c, arg); BCH_IOCTL(disk_set_state, struct bch_ioctl_disk_set_state);
case BCH_IOCTL_DISK_EVACUATE: case BCH_IOCTL_DISK_EVACUATE:
return bch2_ioctl_disk_evacuate(c, arg); BCH_IOCTL(disk_evacuate, struct bch_ioctl_disk);
default: default:
return -ENOTTY; return -ENOTTY;

View File

@ -112,7 +112,6 @@ void bch2_submit_wbio_replicas(struct bch_write_bio *wbio, struct bch_fs *c,
n->bounce = false; n->bounce = false;
n->split = true; n->split = true;
n->put_bio = true; n->put_bio = true;
n->have_io_ref = true;
n->bio.bi_opf = wbio->bio.bi_opf; n->bio.bi_opf = wbio->bio.bi_opf;
__bio_inc_remaining(n->orig); __bio_inc_remaining(n->orig);
} else { } else {
@ -127,7 +126,8 @@ void bch2_submit_wbio_replicas(struct bch_write_bio *wbio, struct bch_fs *c,
n->bio.bi_iter.bi_sector = ptr->offset; n->bio.bi_iter.bi_sector = ptr->offset;
if (likely(percpu_ref_tryget(&ca->io_ref))) { if (likely(percpu_ref_tryget(&ca->io_ref))) {
n->bio.bi_bdev = ca->disk_sb.bdev; n->have_io_ref = true;
n->bio.bi_bdev = ca->disk_sb.bdev;
generic_make_request(&n->bio); generic_make_request(&n->bio);
} else { } else {
n->have_io_ref = false; n->have_io_ref = false;
@ -315,9 +315,8 @@ static void bch2_write_endio(struct bio *bio)
struct bch_dev *ca = wbio->ca; struct bch_dev *ca = wbio->ca;
if (bch2_dev_nonfatal_io_err_on(bio->bi_error, ca, if (bch2_dev_nonfatal_io_err_on(bio->bi_error, ca,
"data write")) { "data write"))
set_closure_fn(cl, bch2_write_io_error, index_update_wq(op)); set_closure_fn(cl, bch2_write_io_error, index_update_wq(op));
}
if (wbio->have_io_ref) if (wbio->have_io_ref)
percpu_ref_put(&ca->io_ref); percpu_ref_put(&ca->io_ref);

View File

@ -170,8 +170,7 @@ redo_peek:
/* The node might have already been rewritten: */ /* The node might have already been rewritten: */
if (b->data->keys.seq == n.seq && if (b->data->keys.seq == n.seq) {
!bkey_cmp(b->key.k.p, n.pos)) {
ret = bch2_btree_node_rewrite(&iter, b, &cl); ret = bch2_btree_node_rewrite(&iter, b, &cl);
if (ret) { if (ret) {
bch2_btree_iter_unlock(&iter); bch2_btree_iter_unlock(&iter);
@ -255,6 +254,10 @@ bch2_journal_seq_blacklisted_new(struct journal *j, u64 seq)
lockdep_assert_held(&j->blacklist_lock); lockdep_assert_held(&j->blacklist_lock);
/*
* When we start the journal, bch2_journal_start() will skip over @seq:
*/
bl = kzalloc(sizeof(*bl), GFP_KERNEL); bl = kzalloc(sizeof(*bl), GFP_KERNEL);
if (!bl) if (!bl)
return NULL; return NULL;
@ -287,16 +290,6 @@ int bch2_journal_seq_should_ignore(struct bch_fs *c, u64 seq, struct btree *b)
BUG_ON(b->level); BUG_ON(b->level);
BUG_ON(seq > journal_seq && test_bit(BCH_FS_INITIAL_GC_DONE, &c->flags)); BUG_ON(seq > journal_seq && test_bit(BCH_FS_INITIAL_GC_DONE, &c->flags));
if (seq <= journal_seq) {
if (list_empty_careful(&j->seq_blacklist))
return 0;
mutex_lock(&j->blacklist_lock);
ret = journal_seq_blacklist_find(j, seq) != NULL;
mutex_unlock(&j->blacklist_lock);
return ret;
}
/* /*
* Decrease this back to j->seq + 2 when we next rev the on disk format: * Decrease this back to j->seq + 2 when we next rev the on disk format:
* increasing it temporarily to work around bug in old kernels * increasing it temporarily to work around bug in old kernels
@ -305,22 +298,27 @@ int bch2_journal_seq_should_ignore(struct bch_fs *c, u64 seq, struct btree *b)
"bset journal seq too far in the future: %llu > %llu", "bset journal seq too far in the future: %llu > %llu",
seq, journal_seq); seq, journal_seq);
bch_verbose(c, "btree node %u:%llu:%llu has future journal sequence number %llu, blacklisting", if (seq <= journal_seq &&
b->btree_id, b->key.k.p.inode, b->key.k.p.offset, seq); list_empty_careful(&j->seq_blacklist))
return 0;
/*
* When we start the journal, bch2_journal_start() will skip over @seq:
*/
mutex_lock(&j->blacklist_lock); mutex_lock(&j->blacklist_lock);
for (i = journal_seq + 1; i <= seq; i++) { if (seq <= journal_seq) {
bl = journal_seq_blacklist_find(j, i) ?: bl = journal_seq_blacklist_find(j, seq);
bch2_journal_seq_blacklisted_new(j, i); if (!bl)
if (!bl) {
ret = -ENOMEM;
goto out; goto out;
} else {
bch_verbose(c, "btree node %u:%llu:%llu has future journal sequence number %llu, blacklisting",
b->btree_id, b->key.k.p.inode, b->key.k.p.offset, seq);
for (i = journal_seq + 1; i <= seq; i++) {
bl = journal_seq_blacklist_find(j, i) ?:
bch2_journal_seq_blacklisted_new(j, i);
if (!bl) {
ret = -ENOMEM;
goto out;
}
} }
} }