Message ID | 1417455190-8778-1-git-send-email-anand.jain@oracle.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi Anand, On 12/01/2014 06:33 PM, Anand Jain wrote: > From: Anand Jain <Anand.Jain@oracle.com> > > Not yet ready for integration, but for review and testing of the new sysfs layout > which is currently under /sys/fs/btrfs/by_fsid > > This patch makes btrfs_fs_devices and btrfs_device information readable > from sysfs. This uses the sysfs group visible entry point to mark > certain attributes visible/hidden depending the FS state (mount/unmounted). > > The new layout is as shown below. > > /sys/fs/btrfs/by_fsid* > ./7b047f4d-c2ce-4f22-94a3-68c09057f1bf* > status > fsid* > missing_devices > num_devices* > open_devices > opened* > rotating > rw_devices > seeding > total_devices* > total_rw_bytes > ./e6701882-220a-4416-98ac-a99f095bddcc* > active_pending > bdev > bytes_used > can_discard > devid* > dev_root_fsid > devstats_valid > dev_totalbytes > generation* > in_fs_metadata > io_align > io_width > missing > name* > nobarriers > replace_tgtdev > sector_size > total_bytes > type > uuid* > writeable > > (* indicates that attribute will be visible even when device is > unmounted but registered with btrfs kernel) Thanks, for working on that; I really like the idea to export more information. - it is possible to put the device uuid under a directory like: by_dev_uuid/, this will help the parsing via script - it is possible to make a directory under /sys/fs/btrfs/by_dev_uuid where a link links to the related device; i.e.: /sys/fs/btrfs/by_dev_uuid/e6701882-220a-4416-98ac-a99f095bddcc -> ../by_fsid/7b047f4d-c2ce-4f22-94a3-68c09057f1bf/by_dev_uuid/e6701882-220a-4416-98ac-a99f095bddc This would help to know which devices are registered by the kernel > > The old kobject <fsid> will be merged into this new 'by_fsid' kobject, > so that older attributes under <fsid> and newer attributed under by_fsid > will be merged together as well. It would be fully backward compatible ? I really like your layout more than the current one, but I think that the current sysfs is like a binary API and so it has to be maintained forever > > v2: added support for device add/delete/replace > rebase on the latest integration branch > > Signed-off-by: Anand Jain <anand.jain@oracle.com> > --- > fs/btrfs/dev-replace.c | 7 + > fs/btrfs/super.c | 15 ++ > fs/btrfs/sysfs.c | 383 +++++++++++++++++++++++++++++++++++++++++++++++++ > fs/btrfs/sysfs.h | 6 + > fs/btrfs/volumes.c | 42 ++++++ > fs/btrfs/volumes.h | 6 + > 6 files changed, 459 insertions(+) > > diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c > index 715a115..31ce3a9 100644 > --- a/fs/btrfs/dev-replace.c > +++ b/fs/btrfs/dev-replace.c > @@ -474,6 +474,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, > u8 uuid_tmp[BTRFS_UUID_SIZE]; > struct btrfs_trans_handle *trans; > int ret = 0; > + char uuid_buf[BTRFS_UUID_UNPARSED_SIZE]; > > /* don't allow cancel or unmount to disturb the finishing procedure */ > mutex_lock(&dev_replace->lock_finishing_cancel_unmount); > @@ -595,7 +596,13 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, > /* replace the sysfs entry */ > btrfs_kobj_rm_device(fs_info, src_device); > btrfs_kobj_add_device(fs_info, tgt_device); > + btrfs_destroy_dev_sysfs(src_device); > btrfs_rm_dev_replace_free_srcdev(fs_info, src_device); > + snprintf(uuid_buf, BTRFS_UUID_UNPARSED_SIZE, "%pU", > + tgt_device->uuid); > + if (kobject_rename(&tgt_device->dev_kobj, uuid_buf)) > + printk(KERN_ERR "BTRFS: sysfs uuid %s rename error\n", > + uuid_buf); > > /* write back the superblocks */ > trans = btrfs_start_transaction(root, 0); > diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c > index 017d92d..918eb9d 100644 > --- a/fs/btrfs/super.c > +++ b/fs/btrfs/super.c > @@ -1389,6 +1389,11 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, > goto error_sec_opts; > } > > + error = btrfs_update_by_fsid_sysfs_group(fs_devices); > + if (error) > + btrfs_warn(fs_info, "sysfs update error during mount: %d", > + error); > + > return root; > > error_close_devices: > @@ -1885,8 +1890,18 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) > static void btrfs_kill_super(struct super_block *sb) > { > struct btrfs_fs_info *fs_info = btrfs_sb(sb); > + struct btrfs_fs_devices *fs_devs = fs_info->fs_devices; > + int error; > + > + set_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags); > + error = btrfs_update_by_fsid_sysfs_group(fs_devs); > + if (error) > + btrfs_warn(fs_info, "sysfs update error during unmount: %d", > + error); > + > kill_anon_super(sb); > free_fs_info(fs_info); > + clear_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags); > } > > static struct file_system_type btrfs_fs_type = { > diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c > index 92db3f6..b658812 100644 > --- a/fs/btrfs/sysfs.c > +++ b/fs/btrfs/sysfs.c > @@ -25,6 +25,7 @@ > #include <linux/bug.h> > #include <linux/genhd.h> > #include <linux/debugfs.h> > +#include <linux/rcustring.h> > > #include "ctree.h" > #include "disk-io.h" > @@ -32,6 +33,18 @@ > #include "sysfs.h" > #include "volumes.h" > > +struct kobject *by_fsid; > +static ssize_t btrfs_dev_attr_show(struct kobject *kobj, > + struct kobj_attribute *a, char *buf); > +static ssize_t btrfs_dev_attr_store(struct kobject *kobj, > + struct kobj_attribute *a, > + const char *buf, size_t count); > +static ssize_t btrfs_fs_devs_attr_show(struct kobject *kobj, > + struct kobj_attribute *a, char *buf); > +static ssize_t btrfs_fs_devs_attr_store(struct kobject *kobj, > + struct kobj_attribute *a, > + const char *buf, size_t count); > + > static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj); > > static u64 get_features(struct btrfs_fs_info *fs_info, > @@ -738,13 +751,383 @@ int btrfs_init_sysfs(void) > init_feature_attrs(); > ret = sysfs_create_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); > > + by_fsid = kobject_create_and_add("by_fsid", &btrfs_kset->kobj); > + > return ret; > } > > void btrfs_exit_sysfs(void) > { > + kobject_put(by_fsid); > sysfs_remove_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); > kset_unregister(btrfs_kset); > debugfs_remove_recursive(btrfs_debugfs_root_dentry); > } > > + > +/******* Add support for by_fsid *******/ > +static ssize_t btrfs_show_uuid(u8 *valptr, char *buf) > +{ > + return snprintf(buf, PAGE_SIZE, "%pU\n", valptr); > +} > + > +static ssize_t btrfs_show_str(char *strptr, char *buf) > +{ > + return snprintf(buf, PAGE_SIZE, "%s\n", strptr); > +} > + > +static ssize_t btrfs_show_u(uint val, char *buf) > +{ > + return snprintf(buf, PAGE_SIZE, "%u\n", val); > +} > + > +static ssize_t btrfs_show_d(int val, char *buf) > +{ > + return snprintf(buf, PAGE_SIZE, "%d\n", val); > +} > + > +static void release_by_fsid_kobj(struct kobject *kobj) > +{ > + > +} > + > +struct kobj_type btrfs_by_fsid_ktype = { > + .sysfs_ops = &kobj_sysfs_ops, > + .release = release_by_fsid_kobj, > +}; > + > +struct btrfs_fs_devs_attr { > + struct kobj_attribute kobj_attr; > +}; > + > +#define to_btrfs_fs_devices(_kobj) container_of(_kobj, struct btrfs_fs_devices, fs_devs_kobj) > + > +#define BTRFS_FS_DEV_ATTR(_name)\ > + static struct btrfs_fs_devs_attr btrfs_fs_devs_attr_##_name = {\ > + .kobj_attr = __INIT_KOBJ_ATTR(_name, S_IRUGO,\ > + btrfs_fs_devs_attr_show,\ > + btrfs_fs_devs_attr_store),\ > + } > + > +BTRFS_FS_DEV_ATTR(fsid); > +BTRFS_FS_DEV_ATTR(num_devices); > +BTRFS_FS_DEV_ATTR(open_devices); > +BTRFS_FS_DEV_ATTR(rw_devices); > +BTRFS_FS_DEV_ATTR(missing_devices); > +BTRFS_FS_DEV_ATTR(total_rw_bytes); > +BTRFS_FS_DEV_ATTR(total_devices); > +BTRFS_FS_DEV_ATTR(opened); > +BTRFS_FS_DEV_ATTR(seeding); > +BTRFS_FS_DEV_ATTR(rotating); > + > +#define BTRFS_FS_DEV_ATTR_PTR(_name) (&btrfs_fs_devs_attr_##_name.kobj_attr.attr) > + > +static struct attribute *btrfs_fs_devs_attrs[] = { > + BTRFS_FS_DEV_ATTR_PTR(fsid), > + BTRFS_FS_DEV_ATTR_PTR(num_devices), > + BTRFS_FS_DEV_ATTR_PTR(open_devices), > + BTRFS_FS_DEV_ATTR_PTR(rw_devices), > + BTRFS_FS_DEV_ATTR_PTR(missing_devices), > + BTRFS_FS_DEV_ATTR_PTR(total_rw_bytes), > + BTRFS_FS_DEV_ATTR_PTR(total_devices), > + BTRFS_FS_DEV_ATTR_PTR(opened), > + BTRFS_FS_DEV_ATTR_PTR(seeding), > + BTRFS_FS_DEV_ATTR_PTR(rotating), > + NULL > +}; > + > +#define BTRFS_FS_DEVS_GET_ATTR_UUID(attr, name, valprt, buf)\ > + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ > + return btrfs_show_uuid(valprt, buf) > +#define BTRFS_FS_DEVS_GET_ATTR_STR(attr, name, strprt, buf)\ > + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ > + return btrfs_show_str(strprt, buf) > +#define BTRFS_FS_DEVS_GET_ATTR_U64(attr, name, valprt, buf)\ > + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ > + return btrfs_show_u64(valprt, NULL, buf) > +#define BTRFS_FS_DEVS_GET_ATTR_U(attr, name, val, buf)\ > + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ > + return btrfs_show_u(val, buf) > +#define BTRFS_FS_DEVS_GET_ATTR_D(attr, name, val, buf)\ > + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ > + return btrfs_show_d(val, buf) > + > +static ssize_t btrfs_fs_devs_attr_show(struct kobject *kobj, > + struct kobj_attribute *a, char *buf) > +{ > + struct btrfs_fs_devices *fs_devs = to_btrfs_fs_devices(kobj); > + > + BTRFS_FS_DEVS_GET_ATTR_UUID(&a->attr, fsid, fs_devs->fsid, buf); > + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, num_devices, &fs_devs->num_devices, buf); > + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, open_devices, &fs_devs->open_devices, buf); > + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, rw_devices, &fs_devs->rw_devices, buf); > + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, missing_devices, &fs_devs->missing_devices, buf); > + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, total_rw_bytes, &fs_devs->total_rw_bytes, buf); > + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, total_devices, &fs_devs->total_devices, buf); > + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, opened, fs_devs->opened, buf); > + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, seeding, fs_devs->seeding, buf); > + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, rotating, fs_devs->rotating, buf); > + > + return 0; > +} > + > +static ssize_t btrfs_fs_devs_attr_store(struct kobject *kobj, > + struct kobj_attribute *a, > + const char *buf, size_t count) > +{ > + /* > + * we might need some of the parameter to be writable > + * but as of now just deny all > + */ > + return -EPERM; > +} > + > + > +static umode_t btrfs_fs_devs_attr_visible(struct kobject *kobj, > + struct attribute *attr, int unused) > +{ > + struct btrfs_fs_devices *fs_devs = to_btrfs_fs_devices(kobj); > + > + /* if device is mounted then all is visible */ > + if (fs_devs->opened && > + !(test_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags))) > + return attr->mode|S_IWUSR; > + > + /* when device is unmounted(ing) show only following set*/ > + if (attr == BTRFS_FS_DEV_ATTR_PTR(num_devices)) > + return attr->mode|S_IWUSR; > + else if (attr == BTRFS_FS_DEV_ATTR_PTR(total_devices)) > + return attr->mode|S_IWUSR; > + else if (attr == BTRFS_FS_DEV_ATTR_PTR(opened)) > + return attr->mode|S_IWUSR; > + else if (attr == BTRFS_FS_DEV_ATTR_PTR(fsid)) > + return attr->mode|S_IWUSR; > + > + return 0; > +} > + > +static const struct attribute_group btrfs_fs_devs_attr_group = { > + .attrs = btrfs_fs_devs_attrs, > + .is_visible = btrfs_fs_devs_attr_visible, > +}; > + > +int btrfs_create_fs_devs_sysfs(struct btrfs_fs_devices *fs_devs) > +{ > + int rc; > + > + rc = kobject_init_and_add(&fs_devs->fs_devs_kobj, &btrfs_by_fsid_ktype, > + by_fsid, "%pU", fs_devs->fsid); > + > + rc = sysfs_create_group(&fs_devs->fs_devs_kobj, &btrfs_fs_devs_attr_group); > + return rc; > +} > + > +int btrfs_update_fs_devs_sysfs(struct btrfs_fs_devices *fs_devs) > +{ > + int rc; > + > + rc = sysfs_update_group(&fs_devs->fs_devs_kobj, &btrfs_fs_devs_attr_group); > + > + return rc; > +} > + > +/**** Do the same for the btrfs_device ****/ > + > +static void release_btrfs_dev_kobj(struct kobject *kobj) > +{ > + > +} > + > +struct kobj_type btrfs_dev_ktype = { > + .sysfs_ops = &kobj_sysfs_ops, > + .release = release_btrfs_dev_kobj, > +}; > + > +struct btrfs_dev_attr { > + struct kobj_attribute kobj_attr; > +}; > + > +#define to_btrfs_device(_kobj) container_of(_kobj, struct btrfs_device, dev_kobj) > + > +#define BTRFS_DEV_ATTR(_name)\ > + static struct btrfs_dev_attr btrfs_dev_attr_##_name = {\ > + .kobj_attr = __INIT_KOBJ_ATTR(_name, S_IRUGO,\ > + btrfs_dev_attr_show,\ > + btrfs_dev_attr_store),\ > + } > + > +BTRFS_DEV_ATTR(uuid); > +BTRFS_DEV_ATTR(name); > +BTRFS_DEV_ATTR(devid); > +BTRFS_DEV_ATTR(dev_root_fsid); > +BTRFS_DEV_ATTR(generation); > +BTRFS_DEV_ATTR(total_bytes); > +BTRFS_DEV_ATTR(dev_totalbytes); > +BTRFS_DEV_ATTR(bytes_used); > +BTRFS_DEV_ATTR(type); > +BTRFS_DEV_ATTR(io_align); > +BTRFS_DEV_ATTR(io_width); > +BTRFS_DEV_ATTR(sector_size); > +BTRFS_DEV_ATTR(writeable); > +BTRFS_DEV_ATTR(in_fs_metadata); > +BTRFS_DEV_ATTR(missing); > +BTRFS_DEV_ATTR(can_discard); > +BTRFS_DEV_ATTR(replace_tgtdev); > +BTRFS_DEV_ATTR(active_pending); > +BTRFS_DEV_ATTR(nobarriers); > +BTRFS_DEV_ATTR(devstats_valid); > +BTRFS_DEV_ATTR(bdev); > + > +#define BTRFS_DEV_ATTR_PTR(_name) (&btrfs_dev_attr_##_name.kobj_attr.attr) > + > +static struct attribute *btrfs_dev_attrs[] = { > + BTRFS_DEV_ATTR_PTR(uuid), > + BTRFS_DEV_ATTR_PTR(name), > + BTRFS_DEV_ATTR_PTR(devid), > + BTRFS_DEV_ATTR_PTR(dev_root_fsid), > + BTRFS_DEV_ATTR_PTR(generation), > + BTRFS_DEV_ATTR_PTR(total_bytes), > + BTRFS_DEV_ATTR_PTR(dev_totalbytes), > + BTRFS_DEV_ATTR_PTR(bytes_used), > + BTRFS_DEV_ATTR_PTR(type), > + BTRFS_DEV_ATTR_PTR(io_align), > + BTRFS_DEV_ATTR_PTR(io_width), > + BTRFS_DEV_ATTR_PTR(sector_size), > + BTRFS_DEV_ATTR_PTR(writeable), > + BTRFS_DEV_ATTR_PTR(in_fs_metadata), > + BTRFS_DEV_ATTR_PTR(missing), > + BTRFS_DEV_ATTR_PTR(can_discard), > + BTRFS_DEV_ATTR_PTR(replace_tgtdev), > + BTRFS_DEV_ATTR_PTR(active_pending), > + BTRFS_DEV_ATTR_PTR(nobarriers), > + BTRFS_DEV_ATTR_PTR(devstats_valid), > + BTRFS_DEV_ATTR_PTR(bdev), > + NULL > +}; > + > +#define BTRFS_DEV_GET_ATTR_UUID(attr, name, valprt, buf)\ > + if (attr == BTRFS_DEV_ATTR_PTR(name))\ > + return btrfs_show_uuid(valprt, buf) > +#define BTRFS_DEV_GET_ATTR_STR(attr, name, strprt, buf)\ > + if (attr == BTRFS_DEV_ATTR_PTR(name))\ > + return btrfs_show_str(strprt, buf) > +#define BTRFS_DEV_GET_ATTR_U64(attr, name, valprt, buf)\ > + if (attr == BTRFS_DEV_ATTR_PTR(name))\ > + return btrfs_show_u64(valprt, NULL, buf) > +#define BTRFS_DEV_GET_ATTR_U(attr, name, val, buf)\ > + if (attr == BTRFS_DEV_ATTR_PTR(name))\ > + return btrfs_show_u(val, buf) > +#define BTRFS_DEV_GET_ATTR_D(attr, name, val, buf)\ > + if (attr == BTRFS_DEV_ATTR_PTR(name))\ > + return btrfs_show_d(val, buf) > +#define BTRFS_DEV_CHECK_ATTR(attr, name)\ > + attr == BTRFS_DEV_ATTR_PTR(name) > + > +static ssize_t btrfs_dev_attr_show(struct kobject *kobj, > + struct kobj_attribute *a, char *buf) > +{ > + struct btrfs_device *dev = to_btrfs_device(kobj); > + > + /* Todo: handle the missing device case */ > + BTRFS_DEV_GET_ATTR_STR(&a->attr, name, rcu_string_dereference(dev->name), buf); > + BTRFS_DEV_GET_ATTR_UUID(&a->attr, uuid, dev->uuid, buf); > + BTRFS_DEV_GET_ATTR_U64(&a->attr, devid, &dev->devid, buf); > + BTRFS_DEV_GET_ATTR_UUID(&a->attr, dev_root_fsid, dev->dev_root->fs_info->fsid, buf); > + BTRFS_DEV_GET_ATTR_U64(&a->attr, generation, &dev->generation, buf); > + BTRFS_DEV_GET_ATTR_U64(&a->attr, total_bytes, &dev->total_bytes, buf); > + BTRFS_DEV_GET_ATTR_U64(&a->attr, dev_totalbytes, &dev->disk_total_bytes, buf); > + BTRFS_DEV_GET_ATTR_U64(&a->attr, bytes_used, &dev->bytes_used, buf); > + BTRFS_DEV_GET_ATTR_U64(&a->attr, type, &dev->type, buf); > + BTRFS_DEV_GET_ATTR_U(&a->attr, io_align, dev->io_align, buf); > + BTRFS_DEV_GET_ATTR_U(&a->attr, sector_size, dev->sector_size, buf); > + BTRFS_DEV_GET_ATTR_D(&a->attr, writeable, dev->writeable, buf); > + BTRFS_DEV_GET_ATTR_D(&a->attr, in_fs_metadata, dev->in_fs_metadata, buf); > + BTRFS_DEV_GET_ATTR_D(&a->attr, missing, dev->missing, buf); > + BTRFS_DEV_GET_ATTR_D(&a->attr, can_discard, dev->can_discard, buf); > + BTRFS_DEV_GET_ATTR_D(&a->attr, replace_tgtdev, dev->is_tgtdev_for_dev_replace, buf); > + BTRFS_DEV_GET_ATTR_D(&a->attr, active_pending, dev->running_pending, buf); > + BTRFS_DEV_GET_ATTR_D(&a->attr, nobarriers, dev->nobarriers, buf); > + BTRFS_DEV_GET_ATTR_D(&a->attr, devstats_valid, dev->dev_stats_valid, buf); > + BTRFS_DEV_GET_ATTR_STR(&a->attr, bdev, dev->bdev ? "not_null":"null", buf); > + > + return 0; > +} > + > +static ssize_t btrfs_dev_attr_store(struct kobject *kobj, > + struct kobj_attribute *a, > + const char *buf, size_t count) > +{ > + /* > + * we might need some of the parameter to be writable > + * but as of now just deny all > + */ > + return -EPERM; > +} > + > +static umode_t btrfs_dev_attr_visible(struct kobject *kobj, > + struct attribute *attr, int unused) > +{ > + struct btrfs_fs_devices *fs_devs = to_btrfs_device(kobj)->fs_devices; > + > + /* if device is mounted then all is visible */ > + if (fs_devs->opened && > + !(test_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags))) > + return attr->mode|S_IWUSR; > + > + /* when device is unmounted only the below attributes are visible */ > + if (attr == BTRFS_DEV_ATTR_PTR(uuid)) > + return attr->mode|S_IWUSR; > + if (attr == BTRFS_DEV_ATTR_PTR(name)) > + return attr->mode|S_IWUSR; > + else if (attr == BTRFS_DEV_ATTR_PTR(devid)) > + return attr->mode|S_IWUSR; > + else if (attr == BTRFS_DEV_ATTR_PTR(generation)) > + return attr->mode|S_IWUSR; > + > + return 0; > +} > + > +static const struct attribute_group btrfs_dev_attr_group = { > + .attrs = btrfs_dev_attrs, > + .is_visible = btrfs_dev_attr_visible, > +}; > + > +void btrfs_destroy_dev_sysfs(struct btrfs_device *dev) > +{ > + sysfs_remove_group(&dev->dev_kobj, &btrfs_dev_attr_group); > + kobject_del(&dev->dev_kobj); > + kobject_put(&dev->dev_kobj); > +} > + > +int btrfs_create_dev_sysfs(struct btrfs_device *dev) > +{ > + int rc; > + > + rc = kobject_init_and_add(&dev->dev_kobj, &btrfs_by_fsid_ktype, > + &dev->fs_devices->fs_devs_kobj, "%pU", dev->uuid); > + > + rc = sysfs_create_group(&dev->dev_kobj, &btrfs_dev_attr_group); > + if (rc) > + kobject_put(&dev->dev_kobj); > + > + return rc; > + > +} > + > +int btrfs_update_dev_sysfs(struct btrfs_device *dev) > +{ > + int rc; > + > + rc = sysfs_update_group(&dev->dev_kobj, &btrfs_dev_attr_group); > + > + return rc; > +} > + > +void btrfs_migrate_dev_kobj(struct kobject *src, struct kobject *dst) > +{ > + struct btrfs_device *dev = to_btrfs_device(src); > + btrfs_destroy_dev_sysfs(dev); > + > + dev = to_btrfs_device(dst); > + btrfs_create_dev_sysfs(dev); > +} > diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h > index f7dd298..9b03f9a 100644 > --- a/fs/btrfs/sysfs.h > +++ b/fs/btrfs/sysfs.h > @@ -74,4 +74,10 @@ int btrfs_kobj_add_device(struct btrfs_fs_info *fs_info, > struct btrfs_device *one_device); > int btrfs_kobj_rm_device(struct btrfs_fs_info *fs_info, > struct btrfs_device *one_device); > +int btrfs_create_fs_devs_sysfs(struct btrfs_fs_devices *fs_devices); > +int btrfs_create_dev_sysfs(struct btrfs_device *dev); > +int btrfs_update_fs_devs_sysfs(struct btrfs_fs_devices *fs_devs); > +int btrfs_update_dev_sysfs(struct btrfs_device *dev); > +void btrfs_destroy_dev_sysfs(struct btrfs_device *dev); > +void btrfs_migrate_dev_kobj(struct kobject *src, struct kobject *dst); > #endif /* _BTRFS_SYSFS_H_ */ > diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c > index d13b253..2f9ea3a 100644 > --- a/fs/btrfs/volumes.c > +++ b/fs/btrfs/volumes.c > @@ -53,6 +53,11 @@ static void btrfs_dev_stat_print_on_load(struct btrfs_device *device); > DEFINE_MUTEX(uuid_mutex); > static LIST_HEAD(fs_uuids); > > +struct list_head *btrfs_get_fs_uuids(void) > +{ > + return &fs_uuids; > +} > + > static void lock_chunks(struct btrfs_root *root) > { > mutex_lock(&root->fs_info->chunk_mutex); > @@ -478,6 +483,9 @@ static noinline int device_list_add(const char *path, > > list_add(&fs_devices->list, &fs_uuids); > > + if (btrfs_create_fs_devs_sysfs(fs_devices)) > + printk(KERN_ERR "BTRFS: create fs_devices sysfs entry failed\n"); > + > device = NULL; > } else { > device = __find_device(&fs_devices->devices, devid, > @@ -509,6 +517,9 @@ static noinline int device_list_add(const char *path, > > ret = 1; > device->fs_devices = fs_devices; > + > + if (btrfs_create_dev_sysfs(device)) > + printk(KERN_ERR "BTRFS: create btrfs_dev sysfs entry failed\n"); > } else if (!device->name || strcmp(device->name->str, path)) { > /* > * When FS is already mounted. > @@ -741,6 +752,16 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) > list_replace_rcu(&device->dev_list, &new_device->dev_list); > new_device->fs_devices = device->fs_devices; > > + /* > + * Todo: > + * its bit ugly that btrfs_device is being deleted and recreated > + * for which we need to delete the sysfs kobject and create it > + * again. which means if users cwd is this sysfs dir, then it > + * would be staled. - need to avoid deleting btrfs_device when > + * closing. > + */ > + btrfs_migrate_dev_kobj(&device->dev_kobj, &new_device->dev_kobj); > + > call_rcu(&device->rcu, free_device); > } > mutex_unlock(&fs_devices->device_list_mutex); > @@ -1703,6 +1724,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) > /* remove sysfs entry */ > btrfs_kobj_rm_device(root->fs_info, device); > } > + btrfs_destroy_dev_sysfs(device); > > call_rcu(&device->rcu, free_device); > > @@ -2207,6 +2229,9 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) > > /* add sysfs device entry */ > btrfs_kobj_add_device(root->fs_info, device); > + /* add the kobject for the new by_fsid layout */ > + if (btrfs_create_dev_sysfs(device)) > + printk(KERN_ERR "BTRFS: create btrfs_dev sysfs entry failed\n"); > > /* > * we've got more storage, clear any full flags on the space > @@ -2381,6 +2406,10 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path, > list_add(&device->dev_list, &fs_info->fs_devices->devices); > fs_info->fs_devices->num_devices++; > fs_info->fs_devices->open_devices++; > + > + if (btrfs_create_dev_sysfs(device)) > + printk(KERN_ERR "BTRFS: sysfs dev create failed for transit device\n"); > + > mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); > > *device_out = device; > @@ -6691,3 +6720,16 @@ void btrfs_update_commit_device_bytes_used(struct btrfs_root *root, > } > unlock_chunks(root); > } > + > +int btrfs_update_by_fsid_sysfs_group(struct btrfs_fs_devices *fs_devs) > +{ > + int rc; > + struct btrfs_device *dev; > + > + rc = btrfs_update_fs_devs_sysfs(fs_devs); > + > + list_for_each_entry(dev, &fs_devs->devices, dev_list) > + rc = btrfs_update_dev_sysfs(dev); > + > + return rc; > +} > diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h > index 6e04f27..bada662 100644 > --- a/fs/btrfs/volumes.h > +++ b/fs/btrfs/volumes.h > @@ -28,6 +28,8 @@ extern struct mutex uuid_mutex; > > #define BTRFS_STRIPE_LEN (64 * 1024) > > +#define BTRFS_FS_DEVS_UNMOUNTING (1ULL << 0) > + > struct buffer_head; > struct btrfs_pending_bios { > struct bio *head; > @@ -150,6 +152,7 @@ struct btrfs_device { > /* Counter to record the change of device stats */ > atomic_t dev_stats_ccnt; > atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX]; > + struct kobject dev_kobj; > }; > > /* > @@ -253,6 +256,8 @@ struct btrfs_fs_devices { > * nonrot flag set > */ > int rotating; > + struct kobject fs_devs_kobj; > + unsigned long flags; > }; > > #define BTRFS_BIO_INLINE_CSUM_SIZE 64 > @@ -523,4 +528,5 @@ static inline void btrfs_dev_stat_reset(struct btrfs_device *dev, > void btrfs_update_commit_device_size(struct btrfs_fs_info *fs_info); > void btrfs_update_commit_device_bytes_used(struct btrfs_root *root, > struct btrfs_transaction *transaction); > +int btrfs_update_by_fsid_sysfs_group(struct btrfs_fs_devices *fs_devs); > #endif >
Hi Goffredo, inline below.. On 02/12/2014 01:29, Goffredo Baroncelli wrote: > Hi Anand, > > On 12/01/2014 06:33 PM, Anand Jain wrote: >> From: Anand Jain <Anand.Jain@oracle.com> >> >> Not yet ready for integration, but for review and testing of the new sysfs layout >> which is currently under /sys/fs/btrfs/by_fsid >> >> This patch makes btrfs_fs_devices and btrfs_device information readable >> from sysfs. This uses the sysfs group visible entry point to mark >> certain attributes visible/hidden depending the FS state (mount/unmounted). >> >> The new layout is as shown below. >> >> /sys/fs/btrfs/by_fsid* >> ./7b047f4d-c2ce-4f22-94a3-68c09057f1bf* >> status >> fsid* >> missing_devices >> num_devices* >> open_devices >> opened* >> rotating >> rw_devices >> seeding >> total_devices* >> total_rw_bytes >> ./e6701882-220a-4416-98ac-a99f095bddcc* >> active_pending >> bdev >> bytes_used >> can_discard >> devid* >> dev_root_fsid >> devstats_valid >> dev_totalbytes >> generation* >> in_fs_metadata >> io_align >> io_width >> missing >> name* >> nobarriers >> replace_tgtdev >> sector_size >> total_bytes >> type >> uuid* >> writeable >> >> (* indicates that attribute will be visible even when device is >> unmounted but registered with btrfs kernel) > > Thanks, for working on that; I really like the idea to export more information. > - it is possible to put the device uuid under a directory like: by_dev_uuid/, > this will help the parsing via script > - it is possible to make a directory under /sys/fs/btrfs/by_dev_uuid where > a link links to the related device; i.e.: > /sys/fs/btrfs/by_dev_uuid/e6701882-220a-4416-98ac-a99f095bddcc -> > ../by_fsid/7b047f4d-c2ce-4f22-94a3-68c09057f1bf/by_dev_uuid/e6701882-220a-4416-98ac-a99f095bddc > > > This would help to know which devices are registered by the kernel > firstly we want the actual file layout so that we could create links further as we find suitable. it can be done. >> >> The old kobject <fsid> will be merged into this new 'by_fsid' kobject, >> so that older attributes under <fsid> and newer attributed under by_fsid >> will be merged together as well. > > It would be fully backward compatible ? I really like your layout more > than the current one, but I think that the current sysfs is like a > binary API and so it has to be maintained forever That was big challenge in this whole effort, yes it will be backward compatible. Thanks, Anand >> >> v2: added support for device add/delete/replace >> rebase on the latest integration branch >> >> Signed-off-by: Anand Jain <anand.jain@oracle.com> >> --- >> fs/btrfs/dev-replace.c | 7 + >> fs/btrfs/super.c | 15 ++ >> fs/btrfs/sysfs.c | 383 +++++++++++++++++++++++++++++++++++++++++++++++++ >> fs/btrfs/sysfs.h | 6 + >> fs/btrfs/volumes.c | 42 ++++++ >> fs/btrfs/volumes.h | 6 + >> 6 files changed, 459 insertions(+) >> >> diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c >> index 715a115..31ce3a9 100644 >> --- a/fs/btrfs/dev-replace.c >> +++ b/fs/btrfs/dev-replace.c >> @@ -474,6 +474,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, >> u8 uuid_tmp[BTRFS_UUID_SIZE]; >> struct btrfs_trans_handle *trans; >> int ret = 0; >> + char uuid_buf[BTRFS_UUID_UNPARSED_SIZE]; >> >> /* don't allow cancel or unmount to disturb the finishing procedure */ >> mutex_lock(&dev_replace->lock_finishing_cancel_unmount); >> @@ -595,7 +596,13 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, >> /* replace the sysfs entry */ >> btrfs_kobj_rm_device(fs_info, src_device); >> btrfs_kobj_add_device(fs_info, tgt_device); >> + btrfs_destroy_dev_sysfs(src_device); >> btrfs_rm_dev_replace_free_srcdev(fs_info, src_device); >> + snprintf(uuid_buf, BTRFS_UUID_UNPARSED_SIZE, "%pU", >> + tgt_device->uuid); >> + if (kobject_rename(&tgt_device->dev_kobj, uuid_buf)) >> + printk(KERN_ERR "BTRFS: sysfs uuid %s rename error\n", >> + uuid_buf); >> >> /* write back the superblocks */ >> trans = btrfs_start_transaction(root, 0); >> diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c >> index 017d92d..918eb9d 100644 >> --- a/fs/btrfs/super.c >> +++ b/fs/btrfs/super.c >> @@ -1389,6 +1389,11 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, >> goto error_sec_opts; >> } >> >> + error = btrfs_update_by_fsid_sysfs_group(fs_devices); >> + if (error) >> + btrfs_warn(fs_info, "sysfs update error during mount: %d", >> + error); >> + >> return root; >> >> error_close_devices: >> @@ -1885,8 +1890,18 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) >> static void btrfs_kill_super(struct super_block *sb) >> { >> struct btrfs_fs_info *fs_info = btrfs_sb(sb); >> + struct btrfs_fs_devices *fs_devs = fs_info->fs_devices; >> + int error; >> + >> + set_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags); >> + error = btrfs_update_by_fsid_sysfs_group(fs_devs); >> + if (error) >> + btrfs_warn(fs_info, "sysfs update error during unmount: %d", >> + error); >> + >> kill_anon_super(sb); >> free_fs_info(fs_info); >> + clear_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags); >> } >> >> static struct file_system_type btrfs_fs_type = { >> diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c >> index 92db3f6..b658812 100644 >> --- a/fs/btrfs/sysfs.c >> +++ b/fs/btrfs/sysfs.c >> @@ -25,6 +25,7 @@ >> #include <linux/bug.h> >> #include <linux/genhd.h> >> #include <linux/debugfs.h> >> +#include <linux/rcustring.h> >> >> #include "ctree.h" >> #include "disk-io.h" >> @@ -32,6 +33,18 @@ >> #include "sysfs.h" >> #include "volumes.h" >> >> +struct kobject *by_fsid; >> +static ssize_t btrfs_dev_attr_show(struct kobject *kobj, >> + struct kobj_attribute *a, char *buf); >> +static ssize_t btrfs_dev_attr_store(struct kobject *kobj, >> + struct kobj_attribute *a, >> + const char *buf, size_t count); >> +static ssize_t btrfs_fs_devs_attr_show(struct kobject *kobj, >> + struct kobj_attribute *a, char *buf); >> +static ssize_t btrfs_fs_devs_attr_store(struct kobject *kobj, >> + struct kobj_attribute *a, >> + const char *buf, size_t count); >> + >> static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj); >> >> static u64 get_features(struct btrfs_fs_info *fs_info, >> @@ -738,13 +751,383 @@ int btrfs_init_sysfs(void) >> init_feature_attrs(); >> ret = sysfs_create_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); >> >> + by_fsid = kobject_create_and_add("by_fsid", &btrfs_kset->kobj); >> + >> return ret; >> } >> >> void btrfs_exit_sysfs(void) >> { >> + kobject_put(by_fsid); >> sysfs_remove_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); >> kset_unregister(btrfs_kset); >> debugfs_remove_recursive(btrfs_debugfs_root_dentry); >> } >> >> + >> +/******* Add support for by_fsid *******/ >> +static ssize_t btrfs_show_uuid(u8 *valptr, char *buf) >> +{ >> + return snprintf(buf, PAGE_SIZE, "%pU\n", valptr); >> +} >> + >> +static ssize_t btrfs_show_str(char *strptr, char *buf) >> +{ >> + return snprintf(buf, PAGE_SIZE, "%s\n", strptr); >> +} >> + >> +static ssize_t btrfs_show_u(uint val, char *buf) >> +{ >> + return snprintf(buf, PAGE_SIZE, "%u\n", val); >> +} >> + >> +static ssize_t btrfs_show_d(int val, char *buf) >> +{ >> + return snprintf(buf, PAGE_SIZE, "%d\n", val); >> +} >> + >> +static void release_by_fsid_kobj(struct kobject *kobj) >> +{ >> + >> +} >> + >> +struct kobj_type btrfs_by_fsid_ktype = { >> + .sysfs_ops = &kobj_sysfs_ops, >> + .release = release_by_fsid_kobj, >> +}; >> + >> +struct btrfs_fs_devs_attr { >> + struct kobj_attribute kobj_attr; >> +}; >> + >> +#define to_btrfs_fs_devices(_kobj) container_of(_kobj, struct btrfs_fs_devices, fs_devs_kobj) >> + >> +#define BTRFS_FS_DEV_ATTR(_name)\ >> + static struct btrfs_fs_devs_attr btrfs_fs_devs_attr_##_name = {\ >> + .kobj_attr = __INIT_KOBJ_ATTR(_name, S_IRUGO,\ >> + btrfs_fs_devs_attr_show,\ >> + btrfs_fs_devs_attr_store),\ >> + } >> + >> +BTRFS_FS_DEV_ATTR(fsid); >> +BTRFS_FS_DEV_ATTR(num_devices); >> +BTRFS_FS_DEV_ATTR(open_devices); >> +BTRFS_FS_DEV_ATTR(rw_devices); >> +BTRFS_FS_DEV_ATTR(missing_devices); >> +BTRFS_FS_DEV_ATTR(total_rw_bytes); >> +BTRFS_FS_DEV_ATTR(total_devices); >> +BTRFS_FS_DEV_ATTR(opened); >> +BTRFS_FS_DEV_ATTR(seeding); >> +BTRFS_FS_DEV_ATTR(rotating); >> + >> +#define BTRFS_FS_DEV_ATTR_PTR(_name) (&btrfs_fs_devs_attr_##_name.kobj_attr.attr) >> + >> +static struct attribute *btrfs_fs_devs_attrs[] = { >> + BTRFS_FS_DEV_ATTR_PTR(fsid), >> + BTRFS_FS_DEV_ATTR_PTR(num_devices), >> + BTRFS_FS_DEV_ATTR_PTR(open_devices), >> + BTRFS_FS_DEV_ATTR_PTR(rw_devices), >> + BTRFS_FS_DEV_ATTR_PTR(missing_devices), >> + BTRFS_FS_DEV_ATTR_PTR(total_rw_bytes), >> + BTRFS_FS_DEV_ATTR_PTR(total_devices), >> + BTRFS_FS_DEV_ATTR_PTR(opened), >> + BTRFS_FS_DEV_ATTR_PTR(seeding), >> + BTRFS_FS_DEV_ATTR_PTR(rotating), >> + NULL >> +}; >> + >> +#define BTRFS_FS_DEVS_GET_ATTR_UUID(attr, name, valprt, buf)\ >> + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ >> + return btrfs_show_uuid(valprt, buf) >> +#define BTRFS_FS_DEVS_GET_ATTR_STR(attr, name, strprt, buf)\ >> + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ >> + return btrfs_show_str(strprt, buf) >> +#define BTRFS_FS_DEVS_GET_ATTR_U64(attr, name, valprt, buf)\ >> + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ >> + return btrfs_show_u64(valprt, NULL, buf) >> +#define BTRFS_FS_DEVS_GET_ATTR_U(attr, name, val, buf)\ >> + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ >> + return btrfs_show_u(val, buf) >> +#define BTRFS_FS_DEVS_GET_ATTR_D(attr, name, val, buf)\ >> + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ >> + return btrfs_show_d(val, buf) >> + >> +static ssize_t btrfs_fs_devs_attr_show(struct kobject *kobj, >> + struct kobj_attribute *a, char *buf) >> +{ >> + struct btrfs_fs_devices *fs_devs = to_btrfs_fs_devices(kobj); >> + >> + BTRFS_FS_DEVS_GET_ATTR_UUID(&a->attr, fsid, fs_devs->fsid, buf); >> + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, num_devices, &fs_devs->num_devices, buf); >> + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, open_devices, &fs_devs->open_devices, buf); >> + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, rw_devices, &fs_devs->rw_devices, buf); >> + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, missing_devices, &fs_devs->missing_devices, buf); >> + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, total_rw_bytes, &fs_devs->total_rw_bytes, buf); >> + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, total_devices, &fs_devs->total_devices, buf); >> + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, opened, fs_devs->opened, buf); >> + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, seeding, fs_devs->seeding, buf); >> + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, rotating, fs_devs->rotating, buf); >> + >> + return 0; >> +} >> + >> +static ssize_t btrfs_fs_devs_attr_store(struct kobject *kobj, >> + struct kobj_attribute *a, >> + const char *buf, size_t count) >> +{ >> + /* >> + * we might need some of the parameter to be writable >> + * but as of now just deny all >> + */ >> + return -EPERM; >> +} >> + >> + >> +static umode_t btrfs_fs_devs_attr_visible(struct kobject *kobj, >> + struct attribute *attr, int unused) >> +{ >> + struct btrfs_fs_devices *fs_devs = to_btrfs_fs_devices(kobj); >> + >> + /* if device is mounted then all is visible */ >> + if (fs_devs->opened && >> + !(test_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags))) >> + return attr->mode|S_IWUSR; >> + >> + /* when device is unmounted(ing) show only following set*/ >> + if (attr == BTRFS_FS_DEV_ATTR_PTR(num_devices)) >> + return attr->mode|S_IWUSR; >> + else if (attr == BTRFS_FS_DEV_ATTR_PTR(total_devices)) >> + return attr->mode|S_IWUSR; >> + else if (attr == BTRFS_FS_DEV_ATTR_PTR(opened)) >> + return attr->mode|S_IWUSR; >> + else if (attr == BTRFS_FS_DEV_ATTR_PTR(fsid)) >> + return attr->mode|S_IWUSR; >> + >> + return 0; >> +} >> + >> +static const struct attribute_group btrfs_fs_devs_attr_group = { >> + .attrs = btrfs_fs_devs_attrs, >> + .is_visible = btrfs_fs_devs_attr_visible, >> +}; >> + >> +int btrfs_create_fs_devs_sysfs(struct btrfs_fs_devices *fs_devs) >> +{ >> + int rc; >> + >> + rc = kobject_init_and_add(&fs_devs->fs_devs_kobj, &btrfs_by_fsid_ktype, >> + by_fsid, "%pU", fs_devs->fsid); >> + >> + rc = sysfs_create_group(&fs_devs->fs_devs_kobj, &btrfs_fs_devs_attr_group); >> + return rc; >> +} >> + >> +int btrfs_update_fs_devs_sysfs(struct btrfs_fs_devices *fs_devs) >> +{ >> + int rc; >> + >> + rc = sysfs_update_group(&fs_devs->fs_devs_kobj, &btrfs_fs_devs_attr_group); >> + >> + return rc; >> +} >> + >> +/**** Do the same for the btrfs_device ****/ >> + >> +static void release_btrfs_dev_kobj(struct kobject *kobj) >> +{ >> + >> +} >> + >> +struct kobj_type btrfs_dev_ktype = { >> + .sysfs_ops = &kobj_sysfs_ops, >> + .release = release_btrfs_dev_kobj, >> +}; >> + >> +struct btrfs_dev_attr { >> + struct kobj_attribute kobj_attr; >> +}; >> + >> +#define to_btrfs_device(_kobj) container_of(_kobj, struct btrfs_device, dev_kobj) >> + >> +#define BTRFS_DEV_ATTR(_name)\ >> + static struct btrfs_dev_attr btrfs_dev_attr_##_name = {\ >> + .kobj_attr = __INIT_KOBJ_ATTR(_name, S_IRUGO,\ >> + btrfs_dev_attr_show,\ >> + btrfs_dev_attr_store),\ >> + } >> + >> +BTRFS_DEV_ATTR(uuid); >> +BTRFS_DEV_ATTR(name); >> +BTRFS_DEV_ATTR(devid); >> +BTRFS_DEV_ATTR(dev_root_fsid); >> +BTRFS_DEV_ATTR(generation); >> +BTRFS_DEV_ATTR(total_bytes); >> +BTRFS_DEV_ATTR(dev_totalbytes); >> +BTRFS_DEV_ATTR(bytes_used); >> +BTRFS_DEV_ATTR(type); >> +BTRFS_DEV_ATTR(io_align); >> +BTRFS_DEV_ATTR(io_width); >> +BTRFS_DEV_ATTR(sector_size); >> +BTRFS_DEV_ATTR(writeable); >> +BTRFS_DEV_ATTR(in_fs_metadata); >> +BTRFS_DEV_ATTR(missing); >> +BTRFS_DEV_ATTR(can_discard); >> +BTRFS_DEV_ATTR(replace_tgtdev); >> +BTRFS_DEV_ATTR(active_pending); >> +BTRFS_DEV_ATTR(nobarriers); >> +BTRFS_DEV_ATTR(devstats_valid); >> +BTRFS_DEV_ATTR(bdev); >> + >> +#define BTRFS_DEV_ATTR_PTR(_name) (&btrfs_dev_attr_##_name.kobj_attr.attr) >> + >> +static struct attribute *btrfs_dev_attrs[] = { >> + BTRFS_DEV_ATTR_PTR(uuid), >> + BTRFS_DEV_ATTR_PTR(name), >> + BTRFS_DEV_ATTR_PTR(devid), >> + BTRFS_DEV_ATTR_PTR(dev_root_fsid), >> + BTRFS_DEV_ATTR_PTR(generation), >> + BTRFS_DEV_ATTR_PTR(total_bytes), >> + BTRFS_DEV_ATTR_PTR(dev_totalbytes), >> + BTRFS_DEV_ATTR_PTR(bytes_used), >> + BTRFS_DEV_ATTR_PTR(type), >> + BTRFS_DEV_ATTR_PTR(io_align), >> + BTRFS_DEV_ATTR_PTR(io_width), >> + BTRFS_DEV_ATTR_PTR(sector_size), >> + BTRFS_DEV_ATTR_PTR(writeable), >> + BTRFS_DEV_ATTR_PTR(in_fs_metadata), >> + BTRFS_DEV_ATTR_PTR(missing), >> + BTRFS_DEV_ATTR_PTR(can_discard), >> + BTRFS_DEV_ATTR_PTR(replace_tgtdev), >> + BTRFS_DEV_ATTR_PTR(active_pending), >> + BTRFS_DEV_ATTR_PTR(nobarriers), >> + BTRFS_DEV_ATTR_PTR(devstats_valid), >> + BTRFS_DEV_ATTR_PTR(bdev), >> + NULL >> +}; >> + >> +#define BTRFS_DEV_GET_ATTR_UUID(attr, name, valprt, buf)\ >> + if (attr == BTRFS_DEV_ATTR_PTR(name))\ >> + return btrfs_show_uuid(valprt, buf) >> +#define BTRFS_DEV_GET_ATTR_STR(attr, name, strprt, buf)\ >> + if (attr == BTRFS_DEV_ATTR_PTR(name))\ >> + return btrfs_show_str(strprt, buf) >> +#define BTRFS_DEV_GET_ATTR_U64(attr, name, valprt, buf)\ >> + if (attr == BTRFS_DEV_ATTR_PTR(name))\ >> + return btrfs_show_u64(valprt, NULL, buf) >> +#define BTRFS_DEV_GET_ATTR_U(attr, name, val, buf)\ >> + if (attr == BTRFS_DEV_ATTR_PTR(name))\ >> + return btrfs_show_u(val, buf) >> +#define BTRFS_DEV_GET_ATTR_D(attr, name, val, buf)\ >> + if (attr == BTRFS_DEV_ATTR_PTR(name))\ >> + return btrfs_show_d(val, buf) >> +#define BTRFS_DEV_CHECK_ATTR(attr, name)\ >> + attr == BTRFS_DEV_ATTR_PTR(name) >> + >> +static ssize_t btrfs_dev_attr_show(struct kobject *kobj, >> + struct kobj_attribute *a, char *buf) >> +{ >> + struct btrfs_device *dev = to_btrfs_device(kobj); >> + >> + /* Todo: handle the missing device case */ >> + BTRFS_DEV_GET_ATTR_STR(&a->attr, name, rcu_string_dereference(dev->name), buf); >> + BTRFS_DEV_GET_ATTR_UUID(&a->attr, uuid, dev->uuid, buf); >> + BTRFS_DEV_GET_ATTR_U64(&a->attr, devid, &dev->devid, buf); >> + BTRFS_DEV_GET_ATTR_UUID(&a->attr, dev_root_fsid, dev->dev_root->fs_info->fsid, buf); >> + BTRFS_DEV_GET_ATTR_U64(&a->attr, generation, &dev->generation, buf); >> + BTRFS_DEV_GET_ATTR_U64(&a->attr, total_bytes, &dev->total_bytes, buf); >> + BTRFS_DEV_GET_ATTR_U64(&a->attr, dev_totalbytes, &dev->disk_total_bytes, buf); >> + BTRFS_DEV_GET_ATTR_U64(&a->attr, bytes_used, &dev->bytes_used, buf); >> + BTRFS_DEV_GET_ATTR_U64(&a->attr, type, &dev->type, buf); >> + BTRFS_DEV_GET_ATTR_U(&a->attr, io_align, dev->io_align, buf); >> + BTRFS_DEV_GET_ATTR_U(&a->attr, sector_size, dev->sector_size, buf); >> + BTRFS_DEV_GET_ATTR_D(&a->attr, writeable, dev->writeable, buf); >> + BTRFS_DEV_GET_ATTR_D(&a->attr, in_fs_metadata, dev->in_fs_metadata, buf); >> + BTRFS_DEV_GET_ATTR_D(&a->attr, missing, dev->missing, buf); >> + BTRFS_DEV_GET_ATTR_D(&a->attr, can_discard, dev->can_discard, buf); >> + BTRFS_DEV_GET_ATTR_D(&a->attr, replace_tgtdev, dev->is_tgtdev_for_dev_replace, buf); >> + BTRFS_DEV_GET_ATTR_D(&a->attr, active_pending, dev->running_pending, buf); >> + BTRFS_DEV_GET_ATTR_D(&a->attr, nobarriers, dev->nobarriers, buf); >> + BTRFS_DEV_GET_ATTR_D(&a->attr, devstats_valid, dev->dev_stats_valid, buf); >> + BTRFS_DEV_GET_ATTR_STR(&a->attr, bdev, dev->bdev ? "not_null":"null", buf); >> + >> + return 0; >> +} >> + >> +static ssize_t btrfs_dev_attr_store(struct kobject *kobj, >> + struct kobj_attribute *a, >> + const char *buf, size_t count) >> +{ >> + /* >> + * we might need some of the parameter to be writable >> + * but as of now just deny all >> + */ >> + return -EPERM; >> +} >> + >> +static umode_t btrfs_dev_attr_visible(struct kobject *kobj, >> + struct attribute *attr, int unused) >> +{ >> + struct btrfs_fs_devices *fs_devs = to_btrfs_device(kobj)->fs_devices; >> + >> + /* if device is mounted then all is visible */ >> + if (fs_devs->opened && >> + !(test_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags))) >> + return attr->mode|S_IWUSR; >> + >> + /* when device is unmounted only the below attributes are visible */ >> + if (attr == BTRFS_DEV_ATTR_PTR(uuid)) >> + return attr->mode|S_IWUSR; >> + if (attr == BTRFS_DEV_ATTR_PTR(name)) >> + return attr->mode|S_IWUSR; >> + else if (attr == BTRFS_DEV_ATTR_PTR(devid)) >> + return attr->mode|S_IWUSR; >> + else if (attr == BTRFS_DEV_ATTR_PTR(generation)) >> + return attr->mode|S_IWUSR; >> + >> + return 0; >> +} >> + >> +static const struct attribute_group btrfs_dev_attr_group = { >> + .attrs = btrfs_dev_attrs, >> + .is_visible = btrfs_dev_attr_visible, >> +}; >> + >> +void btrfs_destroy_dev_sysfs(struct btrfs_device *dev) >> +{ >> + sysfs_remove_group(&dev->dev_kobj, &btrfs_dev_attr_group); >> + kobject_del(&dev->dev_kobj); >> + kobject_put(&dev->dev_kobj); >> +} >> + >> +int btrfs_create_dev_sysfs(struct btrfs_device *dev) >> +{ >> + int rc; >> + >> + rc = kobject_init_and_add(&dev->dev_kobj, &btrfs_by_fsid_ktype, >> + &dev->fs_devices->fs_devs_kobj, "%pU", dev->uuid); >> + >> + rc = sysfs_create_group(&dev->dev_kobj, &btrfs_dev_attr_group); >> + if (rc) >> + kobject_put(&dev->dev_kobj); >> + >> + return rc; >> + >> +} >> + >> +int btrfs_update_dev_sysfs(struct btrfs_device *dev) >> +{ >> + int rc; >> + >> + rc = sysfs_update_group(&dev->dev_kobj, &btrfs_dev_attr_group); >> + >> + return rc; >> +} >> + >> +void btrfs_migrate_dev_kobj(struct kobject *src, struct kobject *dst) >> +{ >> + struct btrfs_device *dev = to_btrfs_device(src); >> + btrfs_destroy_dev_sysfs(dev); >> + >> + dev = to_btrfs_device(dst); >> + btrfs_create_dev_sysfs(dev); >> +} >> diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h >> index f7dd298..9b03f9a 100644 >> --- a/fs/btrfs/sysfs.h >> +++ b/fs/btrfs/sysfs.h >> @@ -74,4 +74,10 @@ int btrfs_kobj_add_device(struct btrfs_fs_info *fs_info, >> struct btrfs_device *one_device); >> int btrfs_kobj_rm_device(struct btrfs_fs_info *fs_info, >> struct btrfs_device *one_device); >> +int btrfs_create_fs_devs_sysfs(struct btrfs_fs_devices *fs_devices); >> +int btrfs_create_dev_sysfs(struct btrfs_device *dev); >> +int btrfs_update_fs_devs_sysfs(struct btrfs_fs_devices *fs_devs); >> +int btrfs_update_dev_sysfs(struct btrfs_device *dev); >> +void btrfs_destroy_dev_sysfs(struct btrfs_device *dev); >> +void btrfs_migrate_dev_kobj(struct kobject *src, struct kobject *dst); >> #endif /* _BTRFS_SYSFS_H_ */ >> diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c >> index d13b253..2f9ea3a 100644 >> --- a/fs/btrfs/volumes.c >> +++ b/fs/btrfs/volumes.c >> @@ -53,6 +53,11 @@ static void btrfs_dev_stat_print_on_load(struct btrfs_device *device); >> DEFINE_MUTEX(uuid_mutex); >> static LIST_HEAD(fs_uuids); >> >> +struct list_head *btrfs_get_fs_uuids(void) >> +{ >> + return &fs_uuids; >> +} >> + >> static void lock_chunks(struct btrfs_root *root) >> { >> mutex_lock(&root->fs_info->chunk_mutex); >> @@ -478,6 +483,9 @@ static noinline int device_list_add(const char *path, >> >> list_add(&fs_devices->list, &fs_uuids); >> >> + if (btrfs_create_fs_devs_sysfs(fs_devices)) >> + printk(KERN_ERR "BTRFS: create fs_devices sysfs entry failed\n"); >> + >> device = NULL; >> } else { >> device = __find_device(&fs_devices->devices, devid, >> @@ -509,6 +517,9 @@ static noinline int device_list_add(const char *path, >> >> ret = 1; >> device->fs_devices = fs_devices; >> + >> + if (btrfs_create_dev_sysfs(device)) >> + printk(KERN_ERR "BTRFS: create btrfs_dev sysfs entry failed\n"); >> } else if (!device->name || strcmp(device->name->str, path)) { >> /* >> * When FS is already mounted. >> @@ -741,6 +752,16 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) >> list_replace_rcu(&device->dev_list, &new_device->dev_list); >> new_device->fs_devices = device->fs_devices; >> >> + /* >> + * Todo: >> + * its bit ugly that btrfs_device is being deleted and recreated >> + * for which we need to delete the sysfs kobject and create it >> + * again. which means if users cwd is this sysfs dir, then it >> + * would be staled. - need to avoid deleting btrfs_device when >> + * closing. >> + */ >> + btrfs_migrate_dev_kobj(&device->dev_kobj, &new_device->dev_kobj); >> + >> call_rcu(&device->rcu, free_device); >> } >> mutex_unlock(&fs_devices->device_list_mutex); >> @@ -1703,6 +1724,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) >> /* remove sysfs entry */ >> btrfs_kobj_rm_device(root->fs_info, device); >> } >> + btrfs_destroy_dev_sysfs(device); >> >> call_rcu(&device->rcu, free_device); >> >> @@ -2207,6 +2229,9 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) >> >> /* add sysfs device entry */ >> btrfs_kobj_add_device(root->fs_info, device); >> + /* add the kobject for the new by_fsid layout */ >> + if (btrfs_create_dev_sysfs(device)) >> + printk(KERN_ERR "BTRFS: create btrfs_dev sysfs entry failed\n"); >> >> /* >> * we've got more storage, clear any full flags on the space >> @@ -2381,6 +2406,10 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path, >> list_add(&device->dev_list, &fs_info->fs_devices->devices); >> fs_info->fs_devices->num_devices++; >> fs_info->fs_devices->open_devices++; >> + >> + if (btrfs_create_dev_sysfs(device)) >> + printk(KERN_ERR "BTRFS: sysfs dev create failed for transit device\n"); >> + >> mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); >> >> *device_out = device; >> @@ -6691,3 +6720,16 @@ void btrfs_update_commit_device_bytes_used(struct btrfs_root *root, >> } >> unlock_chunks(root); >> } >> + >> +int btrfs_update_by_fsid_sysfs_group(struct btrfs_fs_devices *fs_devs) >> +{ >> + int rc; >> + struct btrfs_device *dev; >> + >> + rc = btrfs_update_fs_devs_sysfs(fs_devs); >> + >> + list_for_each_entry(dev, &fs_devs->devices, dev_list) >> + rc = btrfs_update_dev_sysfs(dev); >> + >> + return rc; >> +} >> diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h >> index 6e04f27..bada662 100644 >> --- a/fs/btrfs/volumes.h >> +++ b/fs/btrfs/volumes.h >> @@ -28,6 +28,8 @@ extern struct mutex uuid_mutex; >> >> #define BTRFS_STRIPE_LEN (64 * 1024) >> >> +#define BTRFS_FS_DEVS_UNMOUNTING (1ULL << 0) >> + >> struct buffer_head; >> struct btrfs_pending_bios { >> struct bio *head; >> @@ -150,6 +152,7 @@ struct btrfs_device { >> /* Counter to record the change of device stats */ >> atomic_t dev_stats_ccnt; >> atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX]; >> + struct kobject dev_kobj; >> }; >> >> /* >> @@ -253,6 +256,8 @@ struct btrfs_fs_devices { >> * nonrot flag set >> */ >> int rotating; >> + struct kobject fs_devs_kobj; >> + unsigned long flags; >> }; >> >> #define BTRFS_BIO_INLINE_CSUM_SIZE 64 >> @@ -523,4 +528,5 @@ static inline void btrfs_dev_stat_reset(struct btrfs_device *dev, >> void btrfs_update_commit_device_size(struct btrfs_fs_info *fs_info); >> void btrfs_update_commit_device_bytes_used(struct btrfs_root *root, >> struct btrfs_transaction *transaction); >> +int btrfs_update_by_fsid_sysfs_group(struct btrfs_fs_devices *fs_devs); >> #endif >> > > -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 715a115..31ce3a9 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -474,6 +474,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, u8 uuid_tmp[BTRFS_UUID_SIZE]; struct btrfs_trans_handle *trans; int ret = 0; + char uuid_buf[BTRFS_UUID_UNPARSED_SIZE]; /* don't allow cancel or unmount to disturb the finishing procedure */ mutex_lock(&dev_replace->lock_finishing_cancel_unmount); @@ -595,7 +596,13 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, /* replace the sysfs entry */ btrfs_kobj_rm_device(fs_info, src_device); btrfs_kobj_add_device(fs_info, tgt_device); + btrfs_destroy_dev_sysfs(src_device); btrfs_rm_dev_replace_free_srcdev(fs_info, src_device); + snprintf(uuid_buf, BTRFS_UUID_UNPARSED_SIZE, "%pU", + tgt_device->uuid); + if (kobject_rename(&tgt_device->dev_kobj, uuid_buf)) + printk(KERN_ERR "BTRFS: sysfs uuid %s rename error\n", + uuid_buf); /* write back the superblocks */ trans = btrfs_start_transaction(root, 0); diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 017d92d..918eb9d 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1389,6 +1389,11 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, goto error_sec_opts; } + error = btrfs_update_by_fsid_sysfs_group(fs_devices); + if (error) + btrfs_warn(fs_info, "sysfs update error during mount: %d", + error); + return root; error_close_devices: @@ -1885,8 +1890,18 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) static void btrfs_kill_super(struct super_block *sb) { struct btrfs_fs_info *fs_info = btrfs_sb(sb); + struct btrfs_fs_devices *fs_devs = fs_info->fs_devices; + int error; + + set_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags); + error = btrfs_update_by_fsid_sysfs_group(fs_devs); + if (error) + btrfs_warn(fs_info, "sysfs update error during unmount: %d", + error); + kill_anon_super(sb); free_fs_info(fs_info); + clear_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags); } static struct file_system_type btrfs_fs_type = { diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 92db3f6..b658812 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -25,6 +25,7 @@ #include <linux/bug.h> #include <linux/genhd.h> #include <linux/debugfs.h> +#include <linux/rcustring.h> #include "ctree.h" #include "disk-io.h" @@ -32,6 +33,18 @@ #include "sysfs.h" #include "volumes.h" +struct kobject *by_fsid; +static ssize_t btrfs_dev_attr_show(struct kobject *kobj, + struct kobj_attribute *a, char *buf); +static ssize_t btrfs_dev_attr_store(struct kobject *kobj, + struct kobj_attribute *a, + const char *buf, size_t count); +static ssize_t btrfs_fs_devs_attr_show(struct kobject *kobj, + struct kobj_attribute *a, char *buf); +static ssize_t btrfs_fs_devs_attr_store(struct kobject *kobj, + struct kobj_attribute *a, + const char *buf, size_t count); + static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj); static u64 get_features(struct btrfs_fs_info *fs_info, @@ -738,13 +751,383 @@ int btrfs_init_sysfs(void) init_feature_attrs(); ret = sysfs_create_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); + by_fsid = kobject_create_and_add("by_fsid", &btrfs_kset->kobj); + return ret; } void btrfs_exit_sysfs(void) { + kobject_put(by_fsid); sysfs_remove_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); kset_unregister(btrfs_kset); debugfs_remove_recursive(btrfs_debugfs_root_dentry); } + +/******* Add support for by_fsid *******/ +static ssize_t btrfs_show_uuid(u8 *valptr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%pU\n", valptr); +} + +static ssize_t btrfs_show_str(char *strptr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", strptr); +} + +static ssize_t btrfs_show_u(uint val, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static ssize_t btrfs_show_d(int val, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static void release_by_fsid_kobj(struct kobject *kobj) +{ + +} + +struct kobj_type btrfs_by_fsid_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .release = release_by_fsid_kobj, +}; + +struct btrfs_fs_devs_attr { + struct kobj_attribute kobj_attr; +}; + +#define to_btrfs_fs_devices(_kobj) container_of(_kobj, struct btrfs_fs_devices, fs_devs_kobj) + +#define BTRFS_FS_DEV_ATTR(_name)\ + static struct btrfs_fs_devs_attr btrfs_fs_devs_attr_##_name = {\ + .kobj_attr = __INIT_KOBJ_ATTR(_name, S_IRUGO,\ + btrfs_fs_devs_attr_show,\ + btrfs_fs_devs_attr_store),\ + } + +BTRFS_FS_DEV_ATTR(fsid); +BTRFS_FS_DEV_ATTR(num_devices); +BTRFS_FS_DEV_ATTR(open_devices); +BTRFS_FS_DEV_ATTR(rw_devices); +BTRFS_FS_DEV_ATTR(missing_devices); +BTRFS_FS_DEV_ATTR(total_rw_bytes); +BTRFS_FS_DEV_ATTR(total_devices); +BTRFS_FS_DEV_ATTR(opened); +BTRFS_FS_DEV_ATTR(seeding); +BTRFS_FS_DEV_ATTR(rotating); + +#define BTRFS_FS_DEV_ATTR_PTR(_name) (&btrfs_fs_devs_attr_##_name.kobj_attr.attr) + +static struct attribute *btrfs_fs_devs_attrs[] = { + BTRFS_FS_DEV_ATTR_PTR(fsid), + BTRFS_FS_DEV_ATTR_PTR(num_devices), + BTRFS_FS_DEV_ATTR_PTR(open_devices), + BTRFS_FS_DEV_ATTR_PTR(rw_devices), + BTRFS_FS_DEV_ATTR_PTR(missing_devices), + BTRFS_FS_DEV_ATTR_PTR(total_rw_bytes), + BTRFS_FS_DEV_ATTR_PTR(total_devices), + BTRFS_FS_DEV_ATTR_PTR(opened), + BTRFS_FS_DEV_ATTR_PTR(seeding), + BTRFS_FS_DEV_ATTR_PTR(rotating), + NULL +}; + +#define BTRFS_FS_DEVS_GET_ATTR_UUID(attr, name, valprt, buf)\ + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ + return btrfs_show_uuid(valprt, buf) +#define BTRFS_FS_DEVS_GET_ATTR_STR(attr, name, strprt, buf)\ + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ + return btrfs_show_str(strprt, buf) +#define BTRFS_FS_DEVS_GET_ATTR_U64(attr, name, valprt, buf)\ + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ + return btrfs_show_u64(valprt, NULL, buf) +#define BTRFS_FS_DEVS_GET_ATTR_U(attr, name, val, buf)\ + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ + return btrfs_show_u(val, buf) +#define BTRFS_FS_DEVS_GET_ATTR_D(attr, name, val, buf)\ + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ + return btrfs_show_d(val, buf) + +static ssize_t btrfs_fs_devs_attr_show(struct kobject *kobj, + struct kobj_attribute *a, char *buf) +{ + struct btrfs_fs_devices *fs_devs = to_btrfs_fs_devices(kobj); + + BTRFS_FS_DEVS_GET_ATTR_UUID(&a->attr, fsid, fs_devs->fsid, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, num_devices, &fs_devs->num_devices, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, open_devices, &fs_devs->open_devices, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, rw_devices, &fs_devs->rw_devices, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, missing_devices, &fs_devs->missing_devices, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, total_rw_bytes, &fs_devs->total_rw_bytes, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, total_devices, &fs_devs->total_devices, buf); + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, opened, fs_devs->opened, buf); + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, seeding, fs_devs->seeding, buf); + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, rotating, fs_devs->rotating, buf); + + return 0; +} + +static ssize_t btrfs_fs_devs_attr_store(struct kobject *kobj, + struct kobj_attribute *a, + const char *buf, size_t count) +{ + /* + * we might need some of the parameter to be writable + * but as of now just deny all + */ + return -EPERM; +} + + +static umode_t btrfs_fs_devs_attr_visible(struct kobject *kobj, + struct attribute *attr, int unused) +{ + struct btrfs_fs_devices *fs_devs = to_btrfs_fs_devices(kobj); + + /* if device is mounted then all is visible */ + if (fs_devs->opened && + !(test_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags))) + return attr->mode|S_IWUSR; + + /* when device is unmounted(ing) show only following set*/ + if (attr == BTRFS_FS_DEV_ATTR_PTR(num_devices)) + return attr->mode|S_IWUSR; + else if (attr == BTRFS_FS_DEV_ATTR_PTR(total_devices)) + return attr->mode|S_IWUSR; + else if (attr == BTRFS_FS_DEV_ATTR_PTR(opened)) + return attr->mode|S_IWUSR; + else if (attr == BTRFS_FS_DEV_ATTR_PTR(fsid)) + return attr->mode|S_IWUSR; + + return 0; +} + +static const struct attribute_group btrfs_fs_devs_attr_group = { + .attrs = btrfs_fs_devs_attrs, + .is_visible = btrfs_fs_devs_attr_visible, +}; + +int btrfs_create_fs_devs_sysfs(struct btrfs_fs_devices *fs_devs) +{ + int rc; + + rc = kobject_init_and_add(&fs_devs->fs_devs_kobj, &btrfs_by_fsid_ktype, + by_fsid, "%pU", fs_devs->fsid); + + rc = sysfs_create_group(&fs_devs->fs_devs_kobj, &btrfs_fs_devs_attr_group); + return rc; +} + +int btrfs_update_fs_devs_sysfs(struct btrfs_fs_devices *fs_devs) +{ + int rc; + + rc = sysfs_update_group(&fs_devs->fs_devs_kobj, &btrfs_fs_devs_attr_group); + + return rc; +} + +/**** Do the same for the btrfs_device ****/ + +static void release_btrfs_dev_kobj(struct kobject *kobj) +{ + +} + +struct kobj_type btrfs_dev_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .release = release_btrfs_dev_kobj, +}; + +struct btrfs_dev_attr { + struct kobj_attribute kobj_attr; +}; + +#define to_btrfs_device(_kobj) container_of(_kobj, struct btrfs_device, dev_kobj) + +#define BTRFS_DEV_ATTR(_name)\ + static struct btrfs_dev_attr btrfs_dev_attr_##_name = {\ + .kobj_attr = __INIT_KOBJ_ATTR(_name, S_IRUGO,\ + btrfs_dev_attr_show,\ + btrfs_dev_attr_store),\ + } + +BTRFS_DEV_ATTR(uuid); +BTRFS_DEV_ATTR(name); +BTRFS_DEV_ATTR(devid); +BTRFS_DEV_ATTR(dev_root_fsid); +BTRFS_DEV_ATTR(generation); +BTRFS_DEV_ATTR(total_bytes); +BTRFS_DEV_ATTR(dev_totalbytes); +BTRFS_DEV_ATTR(bytes_used); +BTRFS_DEV_ATTR(type); +BTRFS_DEV_ATTR(io_align); +BTRFS_DEV_ATTR(io_width); +BTRFS_DEV_ATTR(sector_size); +BTRFS_DEV_ATTR(writeable); +BTRFS_DEV_ATTR(in_fs_metadata); +BTRFS_DEV_ATTR(missing); +BTRFS_DEV_ATTR(can_discard); +BTRFS_DEV_ATTR(replace_tgtdev); +BTRFS_DEV_ATTR(active_pending); +BTRFS_DEV_ATTR(nobarriers); +BTRFS_DEV_ATTR(devstats_valid); +BTRFS_DEV_ATTR(bdev); + +#define BTRFS_DEV_ATTR_PTR(_name) (&btrfs_dev_attr_##_name.kobj_attr.attr) + +static struct attribute *btrfs_dev_attrs[] = { + BTRFS_DEV_ATTR_PTR(uuid), + BTRFS_DEV_ATTR_PTR(name), + BTRFS_DEV_ATTR_PTR(devid), + BTRFS_DEV_ATTR_PTR(dev_root_fsid), + BTRFS_DEV_ATTR_PTR(generation), + BTRFS_DEV_ATTR_PTR(total_bytes), + BTRFS_DEV_ATTR_PTR(dev_totalbytes), + BTRFS_DEV_ATTR_PTR(bytes_used), + BTRFS_DEV_ATTR_PTR(type), + BTRFS_DEV_ATTR_PTR(io_align), + BTRFS_DEV_ATTR_PTR(io_width), + BTRFS_DEV_ATTR_PTR(sector_size), + BTRFS_DEV_ATTR_PTR(writeable), + BTRFS_DEV_ATTR_PTR(in_fs_metadata), + BTRFS_DEV_ATTR_PTR(missing), + BTRFS_DEV_ATTR_PTR(can_discard), + BTRFS_DEV_ATTR_PTR(replace_tgtdev), + BTRFS_DEV_ATTR_PTR(active_pending), + BTRFS_DEV_ATTR_PTR(nobarriers), + BTRFS_DEV_ATTR_PTR(devstats_valid), + BTRFS_DEV_ATTR_PTR(bdev), + NULL +}; + +#define BTRFS_DEV_GET_ATTR_UUID(attr, name, valprt, buf)\ + if (attr == BTRFS_DEV_ATTR_PTR(name))\ + return btrfs_show_uuid(valprt, buf) +#define BTRFS_DEV_GET_ATTR_STR(attr, name, strprt, buf)\ + if (attr == BTRFS_DEV_ATTR_PTR(name))\ + return btrfs_show_str(strprt, buf) +#define BTRFS_DEV_GET_ATTR_U64(attr, name, valprt, buf)\ + if (attr == BTRFS_DEV_ATTR_PTR(name))\ + return btrfs_show_u64(valprt, NULL, buf) +#define BTRFS_DEV_GET_ATTR_U(attr, name, val, buf)\ + if (attr == BTRFS_DEV_ATTR_PTR(name))\ + return btrfs_show_u(val, buf) +#define BTRFS_DEV_GET_ATTR_D(attr, name, val, buf)\ + if (attr == BTRFS_DEV_ATTR_PTR(name))\ + return btrfs_show_d(val, buf) +#define BTRFS_DEV_CHECK_ATTR(attr, name)\ + attr == BTRFS_DEV_ATTR_PTR(name) + +static ssize_t btrfs_dev_attr_show(struct kobject *kobj, + struct kobj_attribute *a, char *buf) +{ + struct btrfs_device *dev = to_btrfs_device(kobj); + + /* Todo: handle the missing device case */ + BTRFS_DEV_GET_ATTR_STR(&a->attr, name, rcu_string_dereference(dev->name), buf); + BTRFS_DEV_GET_ATTR_UUID(&a->attr, uuid, dev->uuid, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, devid, &dev->devid, buf); + BTRFS_DEV_GET_ATTR_UUID(&a->attr, dev_root_fsid, dev->dev_root->fs_info->fsid, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, generation, &dev->generation, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, total_bytes, &dev->total_bytes, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, dev_totalbytes, &dev->disk_total_bytes, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, bytes_used, &dev->bytes_used, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, type, &dev->type, buf); + BTRFS_DEV_GET_ATTR_U(&a->attr, io_align, dev->io_align, buf); + BTRFS_DEV_GET_ATTR_U(&a->attr, sector_size, dev->sector_size, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, writeable, dev->writeable, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, in_fs_metadata, dev->in_fs_metadata, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, missing, dev->missing, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, can_discard, dev->can_discard, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, replace_tgtdev, dev->is_tgtdev_for_dev_replace, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, active_pending, dev->running_pending, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, nobarriers, dev->nobarriers, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, devstats_valid, dev->dev_stats_valid, buf); + BTRFS_DEV_GET_ATTR_STR(&a->attr, bdev, dev->bdev ? "not_null":"null", buf); + + return 0; +} + +static ssize_t btrfs_dev_attr_store(struct kobject *kobj, + struct kobj_attribute *a, + const char *buf, size_t count) +{ + /* + * we might need some of the parameter to be writable + * but as of now just deny all + */ + return -EPERM; +} + +static umode_t btrfs_dev_attr_visible(struct kobject *kobj, + struct attribute *attr, int unused) +{ + struct btrfs_fs_devices *fs_devs = to_btrfs_device(kobj)->fs_devices; + + /* if device is mounted then all is visible */ + if (fs_devs->opened && + !(test_bit(BTRFS_FS_DEVS_UNMOUNTING, &fs_devs->flags))) + return attr->mode|S_IWUSR; + + /* when device is unmounted only the below attributes are visible */ + if (attr == BTRFS_DEV_ATTR_PTR(uuid)) + return attr->mode|S_IWUSR; + if (attr == BTRFS_DEV_ATTR_PTR(name)) + return attr->mode|S_IWUSR; + else if (attr == BTRFS_DEV_ATTR_PTR(devid)) + return attr->mode|S_IWUSR; + else if (attr == BTRFS_DEV_ATTR_PTR(generation)) + return attr->mode|S_IWUSR; + + return 0; +} + +static const struct attribute_group btrfs_dev_attr_group = { + .attrs = btrfs_dev_attrs, + .is_visible = btrfs_dev_attr_visible, +}; + +void btrfs_destroy_dev_sysfs(struct btrfs_device *dev) +{ + sysfs_remove_group(&dev->dev_kobj, &btrfs_dev_attr_group); + kobject_del(&dev->dev_kobj); + kobject_put(&dev->dev_kobj); +} + +int btrfs_create_dev_sysfs(struct btrfs_device *dev) +{ + int rc; + + rc = kobject_init_and_add(&dev->dev_kobj, &btrfs_by_fsid_ktype, + &dev->fs_devices->fs_devs_kobj, "%pU", dev->uuid); + + rc = sysfs_create_group(&dev->dev_kobj, &btrfs_dev_attr_group); + if (rc) + kobject_put(&dev->dev_kobj); + + return rc; + +} + +int btrfs_update_dev_sysfs(struct btrfs_device *dev) +{ + int rc; + + rc = sysfs_update_group(&dev->dev_kobj, &btrfs_dev_attr_group); + + return rc; +} + +void btrfs_migrate_dev_kobj(struct kobject *src, struct kobject *dst) +{ + struct btrfs_device *dev = to_btrfs_device(src); + btrfs_destroy_dev_sysfs(dev); + + dev = to_btrfs_device(dst); + btrfs_create_dev_sysfs(dev); +} diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h index f7dd298..9b03f9a 100644 --- a/fs/btrfs/sysfs.h +++ b/fs/btrfs/sysfs.h @@ -74,4 +74,10 @@ int btrfs_kobj_add_device(struct btrfs_fs_info *fs_info, struct btrfs_device *one_device); int btrfs_kobj_rm_device(struct btrfs_fs_info *fs_info, struct btrfs_device *one_device); +int btrfs_create_fs_devs_sysfs(struct btrfs_fs_devices *fs_devices); +int btrfs_create_dev_sysfs(struct btrfs_device *dev); +int btrfs_update_fs_devs_sysfs(struct btrfs_fs_devices *fs_devs); +int btrfs_update_dev_sysfs(struct btrfs_device *dev); +void btrfs_destroy_dev_sysfs(struct btrfs_device *dev); +void btrfs_migrate_dev_kobj(struct kobject *src, struct kobject *dst); #endif /* _BTRFS_SYSFS_H_ */ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index d13b253..2f9ea3a 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -53,6 +53,11 @@ static void btrfs_dev_stat_print_on_load(struct btrfs_device *device); DEFINE_MUTEX(uuid_mutex); static LIST_HEAD(fs_uuids); +struct list_head *btrfs_get_fs_uuids(void) +{ + return &fs_uuids; +} + static void lock_chunks(struct btrfs_root *root) { mutex_lock(&root->fs_info->chunk_mutex); @@ -478,6 +483,9 @@ static noinline int device_list_add(const char *path, list_add(&fs_devices->list, &fs_uuids); + if (btrfs_create_fs_devs_sysfs(fs_devices)) + printk(KERN_ERR "BTRFS: create fs_devices sysfs entry failed\n"); + device = NULL; } else { device = __find_device(&fs_devices->devices, devid, @@ -509,6 +517,9 @@ static noinline int device_list_add(const char *path, ret = 1; device->fs_devices = fs_devices; + + if (btrfs_create_dev_sysfs(device)) + printk(KERN_ERR "BTRFS: create btrfs_dev sysfs entry failed\n"); } else if (!device->name || strcmp(device->name->str, path)) { /* * When FS is already mounted. @@ -741,6 +752,16 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) list_replace_rcu(&device->dev_list, &new_device->dev_list); new_device->fs_devices = device->fs_devices; + /* + * Todo: + * its bit ugly that btrfs_device is being deleted and recreated + * for which we need to delete the sysfs kobject and create it + * again. which means if users cwd is this sysfs dir, then it + * would be staled. - need to avoid deleting btrfs_device when + * closing. + */ + btrfs_migrate_dev_kobj(&device->dev_kobj, &new_device->dev_kobj); + call_rcu(&device->rcu, free_device); } mutex_unlock(&fs_devices->device_list_mutex); @@ -1703,6 +1724,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) /* remove sysfs entry */ btrfs_kobj_rm_device(root->fs_info, device); } + btrfs_destroy_dev_sysfs(device); call_rcu(&device->rcu, free_device); @@ -2207,6 +2229,9 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) /* add sysfs device entry */ btrfs_kobj_add_device(root->fs_info, device); + /* add the kobject for the new by_fsid layout */ + if (btrfs_create_dev_sysfs(device)) + printk(KERN_ERR "BTRFS: create btrfs_dev sysfs entry failed\n"); /* * we've got more storage, clear any full flags on the space @@ -2381,6 +2406,10 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path, list_add(&device->dev_list, &fs_info->fs_devices->devices); fs_info->fs_devices->num_devices++; fs_info->fs_devices->open_devices++; + + if (btrfs_create_dev_sysfs(device)) + printk(KERN_ERR "BTRFS: sysfs dev create failed for transit device\n"); + mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); *device_out = device; @@ -6691,3 +6720,16 @@ void btrfs_update_commit_device_bytes_used(struct btrfs_root *root, } unlock_chunks(root); } + +int btrfs_update_by_fsid_sysfs_group(struct btrfs_fs_devices *fs_devs) +{ + int rc; + struct btrfs_device *dev; + + rc = btrfs_update_fs_devs_sysfs(fs_devs); + + list_for_each_entry(dev, &fs_devs->devices, dev_list) + rc = btrfs_update_dev_sysfs(dev); + + return rc; +} diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 6e04f27..bada662 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -28,6 +28,8 @@ extern struct mutex uuid_mutex; #define BTRFS_STRIPE_LEN (64 * 1024) +#define BTRFS_FS_DEVS_UNMOUNTING (1ULL << 0) + struct buffer_head; struct btrfs_pending_bios { struct bio *head; @@ -150,6 +152,7 @@ struct btrfs_device { /* Counter to record the change of device stats */ atomic_t dev_stats_ccnt; atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX]; + struct kobject dev_kobj; }; /* @@ -253,6 +256,8 @@ struct btrfs_fs_devices { * nonrot flag set */ int rotating; + struct kobject fs_devs_kobj; + unsigned long flags; }; #define BTRFS_BIO_INLINE_CSUM_SIZE 64 @@ -523,4 +528,5 @@ static inline void btrfs_dev_stat_reset(struct btrfs_device *dev, void btrfs_update_commit_device_size(struct btrfs_fs_info *fs_info); void btrfs_update_commit_device_bytes_used(struct btrfs_root *root, struct btrfs_transaction *transaction); +int btrfs_update_by_fsid_sysfs_group(struct btrfs_fs_devices *fs_devs); #endif