[PATCH 4/4] btrfs: sysfs, add devid/dev_state kobject and attribute

Anand Jain
2019-12-05T11:27:22Z

A new sysfs RW attribute
UUID/devinfo/<devid>/dev_state
is added here. The dev_state here reflects the state of the device from
the kernel parameter btrfs_device::dev_state and this attribute is born
after the device is mounted and goes along with the dynamic nature of the
device add and delete. This attribute gets deleted at unmount.

For example:
pwd
/sys/fs/btrfs/6e1961f1-5918-4ecc-a22f-948897b409f7/devinfo
cat 1/dev_state
IN_FS_METADATA MISSING
cat 2/dev_state
WRITABLE IN_FS_METADATA

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
fs/btrfs/sysfs.c | 101 ++++++++++++++++++++++++++++++++++-----------
fs/btrfs/volumes.h | 2 +
2 files changed, 80 insertions(+), 23 deletions(-)

diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c
index 834f712ed60c..99c7269e0524 100644
--- a/fs/btrfs/sysfs.c
+++ b/fs/btrfs/sysfs.c
@@ -978,29 +978,76 @@ int btrfs_sysfs_remove_devices_attr(struct btrfs_fs_devices *fs_devices,
if (!fs_devices->devices_kobj)
return -EINVAL;

- if (one_device && one_device->bdev) {
- disk = one_device->bdev->bd_part;
- disk_kobj = &part_to_dev(disk)->kobj;
+ if (one_device) {
+ if (one_device->bdev) {
+ disk = one_device->bdev->bd_part;
+ disk_kobj = &part_to_dev(disk)->kobj;
+ sysfs_remove_link(fs_devices->devices_kobj, disk_kobj->name);
+ }

- sysfs_remove_link(fs_devices->devices_kobj, disk_kobj->name);
- }
+ kobject_del(&one_device->devid_kobj);
+ kobject_put(&one_device->devid_kobj);

- if (one_device)
return 0;
+ }

- list_for_each_entry(one_device,
- &fs_devices->devices, dev_list) {
- if (!one_device->bdev)
- continue;
- disk = one_device->bdev->bd_part;
- disk_kobj = &part_to_dev(disk)->kobj;
+ list_for_each_entry(one_device, &fs_devices->devices, dev_list) {

- sysfs_remove_link(fs_devices->devices_kobj, disk_kobj->name);
+ if (one_device->bdev) {
+ disk = one_device->bdev->bd_part;
+ disk_kobj = &part_to_dev(disk)->kobj;
+ sysfs_remove_link(fs_devices->devices_kobj, disk_kobj->name);
+ }
+ kobject_del(&one_device->devid_kobj);
+ kobject_put(&one_device->devid_kobj);
}

return 0;
}

+static void btrfs_dev_state_to_str(struct btrfs_device *device, char *dev_state_str, size_t n)
+{
+ int state;
+ const char *btrfs_dev_states[] = { "WRITEABLE", "IN_FS_METADATA",
+ "MISSING", "REPLACE_TGT", "FLUSHING"
+ };
+
+ n = n - strlen(dev_state_str) - 1;
+
+ for (state = 0; state < ARRAY_SIZE(btrfs_dev_states); state++) {
+ if (test_bit(state, &device->dev_state)) {
+ if (strlen(dev_state_str))
+ strncat(dev_state_str, " ", n);
+ strncat(dev_state_str, btrfs_dev_states[state], n);
+ }
+ }
+ strncat(dev_state_str, "\n", n);
+}
+
+static ssize_t btrfs_sysfs_dev_state_show(struct kobject *kobj,
+ struct kobj_attribute *a, char *buf)
+{
+ struct btrfs_device *device = container_of(kobj, struct btrfs_device,
+ devid_kobj);
+
+ btrfs_dev_state_to_str(device, buf, PAGE_SIZE);
+ return strlen(buf);
+}
+
+BTRFS_ATTR(, dev_state, btrfs_sysfs_dev_state_show);
+
+static struct attribute *devinfo_attrs[] = {
+ BTRFS_ATTR_PTR(, dev_state),
+ NULL
+};
+
+ATTRIBUTE_GROUPS(devinfo);
+
+static struct kobj_type devinfo_ktype = {
+ .sysfs_ops = &kobj_sysfs_ops,
+ .default_groups = devinfo_groups,
+};
+
int btrfs_sysfs_add_devices_attr(struct btrfs_fs_devices *fs_devices,
struct btrfs_device *one_device)
{
@@ -1008,22 +1055,30 @@ int btrfs_sysfs_add_devices_attr(struct btrfs_fs_devices *fs_devices,
struct btrfs_device *dev;

list_for_each_entry(dev, &fs_devices->devices, dev_list) {
- struct hd_struct *disk;
- struct kobject *disk_kobj;
-
- if (!dev->bdev)
- continue;

if (one_device && one_device != dev)
continue;

- disk = dev->bdev->bd_part;
- disk_kobj = &part_to_dev(disk)->kobj;
+ if (dev->bdev) {
+ struct hd_struct *disk;
+ struct kobject *disk_kobj;
+
+ disk = dev->bdev->bd_part;
+ disk_kobj = &part_to_dev(disk)->kobj;

- error = sysfs_create_link(fs_devices->devices_kobj,
- disk_kobj, disk_kobj->name);
- if (error)
+ error = sysfs_create_link(fs_devices->devices_kobj,
+ disk_kobj, disk_kobj->name);
+ if (error)
+ break;
+ }
+
+ error = kobject_init_and_add(&dev->devid_kobj, &devinfo_ktype,
+ fs_devices->devinfo_kobj, "%llu",
+ dev->devid);
+ if (error) {
+ kobject_put(&dev->devid_kobj);
break;
+ }
}

return error;
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 38f2e8437b68..68021d1ee216 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -138,6 +138,8 @@ struct btrfs_device {
atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX];

struct extent_io_tree alloc_state;
+
+ struct kobject devid_kobj;
};

/*
--
2.23.0