From patchwork Mon Dec 1 17:33:10 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anand Jain X-Patchwork-Id: 5414021 Return-Path: X-Original-To: patchwork-linux-btrfs@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 4C7519F1C5 for ; Mon, 1 Dec 2014 16:30:46 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 6C4272014A for ; Mon, 1 Dec 2014 16:30:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 57D2420120 for ; Mon, 1 Dec 2014 16:30:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754197AbaLAQaU (ORCPT ); Mon, 1 Dec 2014 11:30:20 -0500 Received: from aserp1040.oracle.com ([141.146.126.69]:32848 "EHLO aserp1040.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752900AbaLAQaP (ORCPT ); Mon, 1 Dec 2014 11:30:15 -0500 Received: from acsinet21.oracle.com (acsinet21.oracle.com [141.146.126.237]) by aserp1040.oracle.com (Sentrion-MTA-4.3.2/Sentrion-MTA-4.3.2) with ESMTP id sB1GUDJk031140 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 1 Dec 2014 16:30:14 GMT Received: from aserz7022.oracle.com (aserz7022.oracle.com [141.146.126.231]) by acsinet21.oracle.com (8.14.4+Sun/8.14.4) with ESMTP id sB1GUC8p029963 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL) for ; Mon, 1 Dec 2014 16:30:13 GMT Received: from abhmp0012.oracle.com (abhmp0012.oracle.com [141.146.116.18]) by aserz7022.oracle.com (8.14.4+Sun/8.14.4) with ESMTP id sB1GUC5A004821 for ; Mon, 1 Dec 2014 16:30:12 GMT Received: from OL.sg.oracle.com (/10.186.101.34) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 01 Dec 2014 08:30:12 -0800 From: Anand Jain To: linux-btrfs@vger.kernel.org Cc: Anand Jain , Anand Jain Subject: [PATCH RFC v2] btrfs: add sysfs layout to show volume info Date: Tue, 2 Dec 2014 01:33:10 +0800 Message-Id: <1417455190-8778-1-git-send-email-anand.jain@oracle.com> X-Mailer: git-send-email 2.0.0.153.g79dcccc In-Reply-To: <1416814173-16945-1-git-send-email-anand.jain@oracle.com> References: <1416814173-16945-1-git-send-email-anand.jain@oracle.com> X-Source-IP: acsinet21.oracle.com [141.146.126.237] Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Anand Jain 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) The old kobject will be merged into this new 'by_fsid' kobject, so that older attributes under and newer attributed under by_fsid will be merged together as well. v2: added support for device add/delete/replace rebase on the latest integration branch Signed-off-by: Anand Jain --- 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 #include #include +#include #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