summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/bcachefs/bcachefs_ioctl.h15
-rw-r--r--fs/bcachefs/bkey_buf.h2
-rw-r--r--fs/bcachefs/buckets.c2
-rw-r--r--fs/bcachefs/chardev.c37
-rw-r--r--fs/bcachefs/darray.c1
-rw-r--r--fs/bcachefs/error.c12
-rw-r--r--fs/bcachefs/journal_reclaim.c2
-rw-r--r--fs/bcachefs/opts.c2
-rw-r--r--fs/bcachefs/replicas.c14
-rw-r--r--fs/bcachefs/replicas.h2
-rw-r--r--fs/bcachefs/sb-members.c4
-rw-r--r--fs/bcachefs/super-io.c4
-rw-r--r--fs/bcachefs/super.c40
-rw-r--r--fs/bcachefs/super.h9
14 files changed, 105 insertions, 41 deletions
diff --git a/fs/bcachefs/bcachefs_ioctl.h b/fs/bcachefs/bcachefs_ioctl.h
index 52594e925eb7..42e2cb5d7b4d 100644
--- a/fs/bcachefs/bcachefs_ioctl.h
+++ b/fs/bcachefs/bcachefs_ioctl.h
@@ -71,6 +71,7 @@ struct bch_ioctl_incremental {
#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_SET_STATE_v2 _IOW(0xbc, 22, struct bch_ioctl_disk_set_state_v2)
#define BCH_IOCTL_DATA _IOW(0xbc, 10, struct bch_ioctl_data)
#define BCH_IOCTL_FS_USAGE _IOWR(0xbc, 11, struct bch_ioctl_fs_usage)
#define BCH_IOCTL_DEV_USAGE _IOWR(0xbc, 11, struct bch_ioctl_dev_usage)
@@ -93,6 +94,12 @@ struct bch_ioctl_incremental {
#define BCHFS_IOC_REINHERIT_ATTRS _IOR(0xbc, 64, const char __user *)
+struct bch_ioctl_err_msg {
+ __u64 msg_ptr;
+ __u32 msg_len;
+ __u32 pad;
+};
+
/*
* BCH_IOCTL_QUERY_UUID: get filesystem UUID
*
@@ -181,6 +188,14 @@ struct bch_ioctl_disk_set_state {
__u64 dev;
};
+struct bch_ioctl_disk_set_state_v2 {
+ __u32 flags;
+ __u8 new_state;
+ __u8 pad[3];
+ __u64 dev;
+ struct bch_ioctl_err_msg err;
+};
+
#define BCH_DATA_OPS() \
x(scrub, 0) \
x(rereplicate, 1) \
diff --git a/fs/bcachefs/bkey_buf.h b/fs/bcachefs/bkey_buf.h
index 0d0c76b013be..0a1fc582f53a 100644
--- a/fs/bcachefs/bkey_buf.h
+++ b/fs/bcachefs/bkey_buf.h
@@ -2,6 +2,8 @@
#ifndef _BCACHEFS_BKEY_BUF_H
#define _BCACHEFS_BKEY_BUF_H
+#include <linux/mempool.h>
+
#include "bcachefs.h"
#include "bkey.h"
diff --git a/fs/bcachefs/buckets.c b/fs/bcachefs/buckets.c
index 87a6f4dce296..280b169efb62 100644
--- a/fs/bcachefs/buckets.c
+++ b/fs/bcachefs/buckets.c
@@ -111,7 +111,7 @@ static int bch2_check_fix_ptr(struct btree_trans *trans,
CLASS(printbuf, buf)();
int ret = 0;
- CLASS(bch2_dev_tryget, ca)(c, p.ptr.dev);
+ CLASS(bch2_dev_tryget_noerror, ca)(c, p.ptr.dev);
if (!ca) {
if (fsck_err_on(p.ptr.dev != BCH_SB_MEMBER_INVALID,
trans, ptr_to_invalid_device,
diff --git a/fs/bcachefs/chardev.c b/fs/bcachefs/chardev.c
index 467fc45e84fe..3b8c1409bbf6 100644
--- a/fs/bcachefs/chardev.c
+++ b/fs/bcachefs/chardev.c
@@ -287,11 +287,44 @@ static long bch2_ioctl_disk_set_state(struct bch_fs *c,
if (IS_ERR(ca))
return PTR_ERR(ca);
- int ret = bch2_dev_set_state(c, ca, arg.new_state, arg.flags);
+ CLASS(printbuf, err)();
+ int ret = bch2_dev_set_state(c, ca, arg.new_state, arg.flags, &err);
bch_err_msg(ca, ret, "setting device state");
return ret;
}
+static long bch2_ioctl_disk_set_state_v2(struct bch_fs *c,
+ struct bch_ioctl_disk_set_state_v2 arg)
+{
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if ((arg.flags & ~(BCH_FORCE_IF_DATA_LOST|
+ BCH_FORCE_IF_METADATA_LOST|
+ BCH_FORCE_IF_DEGRADED|
+ BCH_BY_INDEX)) ||
+ arg.pad[0] || arg.pad[1] || arg.pad[2] ||
+ arg.new_state >= BCH_MEMBER_STATE_NR)
+ return -EINVAL;
+
+ CLASS(bch2_device_lookup, ca)(c, arg.dev, arg.flags);
+ if (IS_ERR(ca))
+ return PTR_ERR(ca);
+
+ CLASS(printbuf, err)();
+ int ret = bch2_dev_set_state(c, ca, arg.new_state, arg.flags, &err);
+ if (ret) {
+ if (err.pos > arg.err.msg_len)
+ return -ERANGE;
+
+ prt_printf(&err, "\nerror=%s", bch2_err_str(ret));
+ ret = copy_to_user_errcode((void __user *)(ulong)arg.err.msg_ptr,
+ err.buf,
+ err.pos) ?: ret;
+ }
+ return ret;
+}
+
struct bch_data_ctx {
struct thread_with_file thr;
@@ -692,6 +725,8 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
BCH_IOCTL(disk_offline, struct bch_ioctl_disk);
case BCH_IOCTL_DISK_SET_STATE:
BCH_IOCTL(disk_set_state, struct bch_ioctl_disk_set_state);
+ case BCH_IOCTL_DISK_SET_STATE_v2:
+ BCH_IOCTL(disk_set_state_v2, struct bch_ioctl_disk_set_state_v2);
case BCH_IOCTL_DATA:
BCH_IOCTL(data, struct bch_ioctl_data);
case BCH_IOCTL_DISK_RESIZE:
diff --git a/fs/bcachefs/darray.c b/fs/bcachefs/darray.c
index c06c81e842e4..6940037bd19e 100644
--- a/fs/bcachefs/darray.c
+++ b/fs/bcachefs/darray.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/log2.h>
+#include <linux/rcupdate.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include "darray.h"
diff --git a/fs/bcachefs/error.c b/fs/bcachefs/error.c
index 32a286b3a74e..e33f3166c48a 100644
--- a/fs/bcachefs/error.c
+++ b/fs/bcachefs/error.c
@@ -141,14 +141,16 @@ void bch2_io_error_work(struct work_struct *work)
if (ca->mi.state >= BCH_MEMBER_STATE_ro)
return;
- bool dev = !__bch2_dev_set_state(c, ca, BCH_MEMBER_STATE_ro,
- BCH_FORCE_IF_DEGRADED);
CLASS(printbuf, buf)();
__bch2_log_msg_start(ca->name, &buf);
- prt_printf(&buf, "writes erroring for %u seconds, setting %s ro",
- c->opts.write_error_timeout,
- dev ? "device" : "filesystem");
+ prt_printf(&buf, "writes erroring for %u seconds\n",
+ c->opts.write_error_timeout);
+
+ bool dev = !__bch2_dev_set_state(c, ca, BCH_MEMBER_STATE_ro,
+ BCH_FORCE_IF_DEGRADED, &buf);
+
+ prt_printf(&buf, "setting %s ro", dev ? "device" : "filesystem");
if (!dev)
bch2_fs_emergency_read_only2(c, &buf);
diff --git a/fs/bcachefs/journal_reclaim.c b/fs/bcachefs/journal_reclaim.c
index 6400a63ed79b..bd1885607d3e 100644
--- a/fs/bcachefs/journal_reclaim.c
+++ b/fs/bcachefs/journal_reclaim.c
@@ -148,7 +148,7 @@ static struct journal_space __journal_space_available(struct journal *j, unsigne
BUG_ON(nr_devs_want > ARRAY_SIZE(dev_space));
- ssize_t mem_limit = max_t(ssize_t, 0,
+ size_t mem_limit = max_t(ssize_t, 0,
(totalram_pages() * PAGE_SIZE) / 4 - j->dirty_entry_bytes);
for_each_member_device_rcu(c, ca, &c->rw_devs[BCH_DATA_journal]) {
diff --git a/fs/bcachefs/opts.c b/fs/bcachefs/opts.c
index 921f9049912d..c3ef35dc01e2 100644
--- a/fs/bcachefs/opts.c
+++ b/fs/bcachefs/opts.c
@@ -525,7 +525,7 @@ int bch2_opt_hook_pre_set(struct bch_fs *c, struct bch_dev *ca, enum bch_opt_id
switch (id) {
case Opt_state:
if (ca)
- return bch2_dev_set_state(c, ca, v, BCH_FORCE_IF_DEGRADED);
+ return bch2_dev_set_state(c, ca, v, BCH_FORCE_IF_DEGRADED, NULL);
break;
case Opt_compression:
diff --git a/fs/bcachefs/replicas.c b/fs/bcachefs/replicas.c
index 0784283ce78c..3ffd68d2608d 100644
--- a/fs/bcachefs/replicas.c
+++ b/fs/bcachefs/replicas.c
@@ -784,7 +784,7 @@ const struct bch_sb_field_ops bch_sb_field_ops_replicas_v0 = {
/* Query replicas: */
bool bch2_have_enough_devs(struct bch_fs *c, struct bch_devs_mask devs,
- unsigned flags, bool print)
+ unsigned flags, struct printbuf *err)
{
struct bch_replicas_entry_v1 *e;
@@ -823,16 +823,14 @@ bool bch2_have_enough_devs(struct bch_fs *c, struct bch_devs_mask devs,
: BCH_FORCE_IF_DATA_DEGRADED;
if (dflags & ~flags) {
- if (print) {
- CLASS(printbuf, buf)();
-
- bch2_replicas_entry_to_text(&buf, e);
- bch_err(c, "insufficient devices online (%u) for replicas entry %s",
- nr_online, buf.buf);
+ if (err) {
+ prt_printf(err, "insufficient devices online (%u) for replicas entry ",
+ nr_online);
+ bch2_replicas_entry_to_text(err, e);
+ prt_newline(err);
}
return false;
}
-
}
return true;
diff --git a/fs/bcachefs/replicas.h b/fs/bcachefs/replicas.h
index 5aba2c1ce133..15023a9b0b1e 100644
--- a/fs/bcachefs/replicas.h
+++ b/fs/bcachefs/replicas.h
@@ -44,7 +44,7 @@ static inline void bch2_replicas_entry_cached(struct bch_replicas_entry_v1 *e,
}
bool bch2_have_enough_devs(struct bch_fs *, struct bch_devs_mask,
- unsigned, bool);
+ unsigned, struct printbuf *);
unsigned bch2_sb_dev_has_data(struct bch_sb *, unsigned);
unsigned bch2_dev_has_data(struct bch_fs *, struct bch_dev *);
diff --git a/fs/bcachefs/sb-members.c b/fs/bcachefs/sb-members.c
index e3c73d903898..d26a0ca4a59d 100644
--- a/fs/bcachefs/sb-members.c
+++ b/fs/bcachefs/sb-members.c
@@ -36,10 +36,12 @@ int bch2_dev_missing_bkey(struct bch_fs *c, struct bkey_s_c k, unsigned dev)
void bch2_dev_missing_atomic(struct bch_fs *c, unsigned dev)
{
- if (dev != BCH_SB_MEMBER_INVALID)
+ if (dev != BCH_SB_MEMBER_INVALID) {
bch2_fs_inconsistent(c, "pointer to %s device %u",
test_bit(dev, c->devs_removed.d)
? "removed" : "nonexistent", dev);
+ dump_stack();
+ }
}
void bch2_dev_bucket_missing(struct bch_dev *ca, u64 bucket)
diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c
index 5897380c4c08..61eeac671283 100644
--- a/fs/bcachefs/super-io.c
+++ b/fs/bcachefs/super-io.c
@@ -1189,13 +1189,13 @@ int bch2_write_super(struct bch_fs *c)
nr_wrote = dev_mask_nr(&sb_written);
can_mount_with_written =
- bch2_have_enough_devs(c, sb_written, degraded_flags, false);
+ bch2_have_enough_devs(c, sb_written, degraded_flags, NULL);
for (unsigned i = 0; i < ARRAY_SIZE(sb_written.d); i++)
sb_written.d[i] = ~sb_written.d[i];
can_mount_without_written =
- bch2_have_enough_devs(c, sb_written, degraded_flags, false);
+ bch2_have_enough_devs(c, sb_written, degraded_flags, NULL);
/*
* If we would be able to mount _without_ the devices we successfully
diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c
index ee3b30b1c2b5..4281a20f7856 100644
--- a/fs/bcachefs/super.c
+++ b/fs/bcachefs/super.c
@@ -1368,10 +1368,14 @@ static bool bch2_fs_may_start(struct bch_fs *c)
return false;
}
break;
- }
+ }
}
- return bch2_have_enough_devs(c, c->online_devs, flags, true);
+ CLASS(printbuf, err)();
+ bool ret = bch2_have_enough_devs(c, c->online_devs, flags, &err);
+ if (!ret)
+ bch2_print_str(c, KERN_ERR, err.buf);
+ return ret;
}
int bch2_fs_start(struct bch_fs *c)
@@ -1833,7 +1837,8 @@ static int bch2_dev_attach_bdev(struct bch_fs *c, struct bch_sb_handle *sb)
* because we got an error or what have you?
*/
bool bch2_dev_state_allowed(struct bch_fs *c, struct bch_dev *ca,
- enum bch_member_state new_state, int flags)
+ enum bch_member_state new_state, int flags,
+ struct printbuf *err)
{
struct bch_devs_mask new_online_devs;
int nr_rw = 0, required;
@@ -1870,7 +1875,7 @@ bool bch2_dev_state_allowed(struct bch_fs *c, struct bch_dev *ca,
new_online_devs = c->online_devs;
__clear_bit(ca->dev_idx, new_online_devs.d);
- return bch2_have_enough_devs(c, new_online_devs, flags, false);
+ return bch2_have_enough_devs(c, new_online_devs, flags, err);
default:
BUG();
}
@@ -1904,14 +1909,15 @@ static void __bch2_dev_read_write(struct bch_fs *c, struct bch_dev *ca)
}
int __bch2_dev_set_state(struct bch_fs *c, struct bch_dev *ca,
- enum bch_member_state new_state, int flags)
+ enum bch_member_state new_state, int flags,
+ struct printbuf *err)
{
int ret = 0;
if (ca->mi.state == new_state)
return 0;
- if (!bch2_dev_state_allowed(c, ca, new_state, flags))
+ if (!bch2_dev_state_allowed(c, ca, new_state, flags, err))
return bch_err_throw(c, device_state_not_allowed);
if (new_state != BCH_MEMBER_STATE_rw)
@@ -1934,10 +1940,11 @@ int __bch2_dev_set_state(struct bch_fs *c, struct bch_dev *ca,
}
int bch2_dev_set_state(struct bch_fs *c, struct bch_dev *ca,
- enum bch_member_state new_state, int flags)
+ enum bch_member_state new_state, int flags,
+ struct printbuf *err)
{
guard(rwsem_write)(&c->state_lock);
- return __bch2_dev_set_state(c, ca, new_state, flags);
+ return __bch2_dev_set_state(c, ca, new_state, flags, err);
}
/* Device add/removal: */
@@ -1957,7 +1964,7 @@ int bch2_dev_remove(struct bch_fs *c, struct bch_dev *ca, int flags)
*/
bch2_dev_put(ca);
- if (!bch2_dev_state_allowed(c, ca, BCH_MEMBER_STATE_failed, flags)) {
+ if (!bch2_dev_state_allowed(c, ca, BCH_MEMBER_STATE_failed, flags, NULL)) {
bch_err(ca, "Cannot remove without losing data");
ret = bch_err_throw(c, device_state_not_allowed);
goto err;
@@ -2278,7 +2285,7 @@ int bch2_dev_offline(struct bch_fs *c, struct bch_dev *ca, int flags)
return 0;
}
- if (!bch2_dev_state_allowed(c, ca, BCH_MEMBER_STATE_failed, flags)) {
+ if (!bch2_dev_state_allowed(c, ca, BCH_MEMBER_STATE_failed, flags, NULL)) {
bch_err(ca, "Cannot offline required disk");
return bch_err_throw(c, device_state_not_allowed);
}
@@ -2455,10 +2462,14 @@ static void bch2_fs_bdev_mark_dead(struct block_device *bdev, bool surprise)
struct bch_dev *ca = bdev_to_bch_dev(c, bdev);
if (ca) {
+ CLASS(printbuf, buf)();
+ __bch2_log_msg_start(ca->name, &buf);
+ prt_printf(&buf, "offline from block layer\n");
+
bool dev = bch2_dev_state_allowed(c, ca,
BCH_MEMBER_STATE_failed,
- BCH_FORCE_IF_DEGRADED);
-
+ BCH_FORCE_IF_DEGRADED,
+ &buf);
if (!dev && sb) {
if (!surprise)
sync_filesystem(sb);
@@ -2466,11 +2477,6 @@ static void bch2_fs_bdev_mark_dead(struct block_device *bdev, bool surprise)
evict_inodes(sb);
}
- CLASS(printbuf, buf)();
- __bch2_log_msg_start(ca->name, &buf);
-
- prt_printf(&buf, "offline from block layer");
-
if (dev) {
__bch2_dev_offline(c, ca);
} else {
diff --git a/fs/bcachefs/super.h b/fs/bcachefs/super.h
index e90bab9afe78..de2c4430b8f7 100644
--- a/fs/bcachefs/super.h
+++ b/fs/bcachefs/super.h
@@ -17,11 +17,14 @@ struct bch_fs *bch2_dev_to_fs(dev_t);
struct bch_fs *bch2_uuid_to_fs(__uuid_t);
bool bch2_dev_state_allowed(struct bch_fs *, struct bch_dev *,
- enum bch_member_state, int);
+ enum bch_member_state, int,
+ struct printbuf *);
int __bch2_dev_set_state(struct bch_fs *, struct bch_dev *,
- enum bch_member_state, int);
+ enum bch_member_state, int,
+ struct printbuf *);
int bch2_dev_set_state(struct bch_fs *, struct bch_dev *,
- enum bch_member_state, int);
+ enum bch_member_state, int,
+ struct printbuf *);
int bch2_dev_fail(struct bch_dev *, int);
int bch2_dev_remove(struct bch_fs *, struct bch_dev *, int);