Hi all, The following series restores the BTRFS sysfs support. My idea is to exporting some information via sysfs to get rid of some ioctl(s) which are particular solution of a more general problem: exporting information from kernel space to user space. These patches export basically the fields of the following structure: - btrfs_device - btrfs_fs_info - btrfs_fs_devices You can read more about which information are exported reading the patch (the last one) which contains an update of the btrfs.txt file. The goal of this first attempt is to collect comments relating to which information we should export and which not or the sysfs structure. For now these information are read-only. In the future we could consider the idea to make some action: for example to change the label of a filesystem. Comments are welcome. BR G.Baroncelli --- Goffredo Baroncelli (4): Add support for sysfs to btrfs. Add hook for sysfs to btrfs. Add a new Kconfig section to enable or disable the sysfs. Btrfs sysfs support documentation. fs/btrfs/Kconfig | 9 + fs/btrfs/super.c | 4 fs/btrfs/sysfs.c | 933 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/sysfs.h | 31 ++ fs/btrfs/volumes.c | 15 + 5 files changed, 986 insertions(+), 6 deletions(-) create mode 100644 fs/btrfs/sysfs.h -- 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
Export via sysfs some information about the btrfs devices and filesystem. --- fs/btrfs/super.c | 4 fs/btrfs/sysfs.c | 933 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/sysfs.h | 31 ++ 3 files changed, 963 insertions(+), 5 deletions(-) create mode 100644 fs/btrfs/sysfs.h diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index e239915..8927674 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -55,6 +55,7 @@ #include "export.h" #include "compression.h" #include "rcu-string.h" +#include "sysfs.h" #define CREATE_TRACE_POINTS #include <trace/events/btrfs.h> @@ -1093,6 +1094,8 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, if (IS_ERR(root)) deactivate_locked_super(s); + btrfs_sysfs_add_filesystem(fs_info); + return root; error_close_devices: @@ -1422,6 +1425,7 @@ static void btrfs_kill_super(struct super_block *sb) { struct btrfs_fs_info *fs_info = btrfs_sb(sb); kill_anon_super(sb); + btrfs_sysfs_remove_filesystem(fs_info); free_fs_info(fs_info); } diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index daac9ae..fb582e1 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -24,23 +24,946 @@ #include <linux/module.h> #include <linux/kobject.h> +#include <linux/list.h> + + #include "ctree.h" +#include "volumes.h" #include "disk-io.h" #include "transaction.h" +#include "rcu-string.h" + +#include "sysfs.h" + +#ifdef CONFIG_BTRFS_FS_SYSFS + +static struct kobject *btrfs_kobject=0; +static struct kobject *btrfs_devices_kobj=0; +static struct kobject *btrfs_filesystems_kobj=0; + +struct btrfs_sysfs_device { + struct kobject kobj; + struct list_head list; + struct btrfs_device *dev; +}; + +struct btrfs_sysfs_filesystem { + struct kobject kobj; + struct list_head list; + struct btrfs_fs_info *fs_info; +}; + +struct btrfs_sysfs_fs_devices { + struct kobject kobj; + struct list_head list; + struct btrfs_fs_devices *fs_devices; +}; + +struct btrfs_sysfs_fsid { + struct kobject *kobj; + struct list_head list; + u8 fsid[BTRFS_FSID_SIZE]; +}; + +static struct list_head btrfs_sysfs_device_list; +static struct list_head btrfs_sysfs_fs_devices_list; +static struct list_head btrfs_sysfs_filesystem_list; +static struct list_head btrfs_sysfs_fsid_list; + + +void uuid_unparse( u8 *uuid, char *out ){ + static char *i2x = "0123456789abcdef"; + static int lengths[] = {4,2,2,2,6,0}; + int i; + + for(i=0; ; i++){ + int j; + for(j=0; j < lengths[i] ; j++, uuid++){ + *out++ = i2x[*uuid >> 4]; + *out++ = i2x[*uuid & 0x0f]; + } + if( !lengths[i+1] ){ + *out = 0; + break; + }else{ + *out++ = ''-''; + } + } +} -/* /sys/fs/btrfs/ entry */ -static struct kset *btrfs_kset; +static struct btrfs_sysfs_fsid *find_fsid( u8 *fsid){ + struct btrfs_sysfs_fsid *bs_fsid; + list_for_each_entry(bs_fsid, &btrfs_sysfs_fsid_list, list) + if(!memcmp(bs_fsid->fsid, fsid, BTRFS_FSID_SIZE)) + return bs_fsid; + + return NULL; +} + +static struct btrfs_sysfs_fsid *add_fsid( u8 *fsid){ + + struct btrfs_sysfs_fsid *o_fsid; + char buf[BTRFS_FSID_SIZE*2+5]; + + o_fsid = find_fsid(fsid); + if(o_fsid) return o_fsid; + + uuid_unparse(fsid, buf); + o_fsid = kzalloc(sizeof(struct btrfs_sysfs_fsid),GFP_NOFS); + if( !o_fsid ){ + printk("btrfs_sysfs: cannot register the fsid ''%s''\n", buf); + return NULL; + } + + o_fsid->kobj = kobject_create_and_add(buf, btrfs_filesystems_kobj); + if(!o_fsid->kobj){ + printk("btrfs_sysfs: cannot allocate the kobject fsid ''%s''\n", buf); + kfree(o_fsid); + return NULL; + } + memcpy(o_fsid->fsid, fsid, BTRFS_FSID_SIZE); + list_add(&o_fsid->list, &btrfs_sysfs_fsid_list); + + return o_fsid; +} + +static void del_fsid( u8 *fsid){ + struct btrfs_sysfs_fsid *o_fsid; + struct btrfs_sysfs_device *bs_dev; + + o_fsid = find_fsid(fsid); + if(!o_fsid){ + printk("btrfs_sysfs: cannot find the fsid to remove\n"); + return; + } + + /* check if the fsid is still in use from another device */ + list_for_each_entry(bs_dev, &btrfs_sysfs_device_list, list){ + if(! bs_dev->dev ) continue; + if(!memcmp(bs_dev->dev->fs_devices->fsid, + fsid, BTRFS_FSID_SIZE)) + return; + } + + kobject_put(o_fsid->kobj); + kfree(o_fsid); + list_del_init(&o_fsid->list); +} int btrfs_init_sysfs(void) { - btrfs_kset = kset_create_and_add("btrfs", NULL, fs_kobj); - if (!btrfs_kset) + + btrfs_kobject = kobject_create_and_add("btrfs", fs_kobj); + if( !btrfs_kobject ){ + btrfs_exit_sysfs( ); + return -ENOMEM; + } + btrfs_devices_kobj = kobject_create_and_add("devices", + btrfs_kobject); + btrfs_filesystems_kobj = kobject_create_and_add("filesystems", + btrfs_kobject); + if( !btrfs_devices_kobj || !btrfs_filesystems_kobj ){ + btrfs_exit_sysfs( ); return -ENOMEM; + } + + INIT_LIST_HEAD(&btrfs_sysfs_device_list); + INIT_LIST_HEAD(&btrfs_sysfs_fs_devices_list); + INIT_LIST_HEAD(&btrfs_sysfs_filesystem_list); + INIT_LIST_HEAD(&btrfs_sysfs_fsid_list); return 0; } +static void destroy_all_devices(void){ + struct btrfs_sysfs_device *bs_dev, *next; + list_for_each_entry_safe(bs_dev, next, + &btrfs_sysfs_device_list, list){ + + list_del_init(&bs_dev->list); + bs_dev->dev = 0; + kobject_put(&bs_dev->kobj); + + } +} + +static void destroy_all_fsid(void){ + struct btrfs_sysfs_fsid *o_fsid, *next; + list_for_each_entry_safe(o_fsid, next, + &btrfs_sysfs_fsid_list, list){ + + list_del_init(&o_fsid->list); + kobject_put(o_fsid->kobj); + } +} + + +static void destroy_all_fs_devices(void){ + struct btrfs_sysfs_fs_devices *bs_fsd, *next; + list_for_each_entry_safe(bs_fsd, next, + &btrfs_sysfs_fs_devices_list, list){ + bs_fsd->fs_devices = 0; + kobject_put(&bs_fsd->kobj); + list_del_init(&bs_fsd->list); + } +} + +static void destroy_all_filesystem(void){ + struct btrfs_sysfs_filesystem *bs_fs, *next; + list_for_each_entry_safe(bs_fs, next, + &btrfs_sysfs_filesystem_list, list){ + bs_fs->fs_info = 0; + kobject_put(&bs_fs->kobj); + list_del_init(&bs_fs->list); + } +} + +void btrfs_exit_sysfs(void) +{ + destroy_all_filesystem( ); + destroy_all_fs_devices( ); + destroy_all_devices( ); + destroy_all_fsid(); + + if(btrfs_filesystems_kobj) + kobject_put(btrfs_filesystems_kobj); + if(btrfs_devices_kobj) + kobject_put(btrfs_devices_kobj); + if(btrfs_kobject) + kobject_put(btrfs_kobject); + + btrfs_filesystems_kobj=0; + btrfs_devices_kobj=0; + btrfs_kobject=0; + +} + +/* + * ******************************************************************* + * Sysfs code related to the fs_devices info + * ******************************************************************* + */ + +struct sysfs_fs_devices_attribute { + struct attribute attr; + ssize_t (*show)(struct btrfs_sysfs_fs_devices *bs_fs, + struct sysfs_fs_devices_attribute *attr, char *buf); + ssize_t (*store)(struct btrfs_sysfs_fs_devices *bs_fs, + struct sysfs_fs_devices_attribute *attr, + const char *buf, size_t count); +}; + +/* + * The default show function that must be passed to sysfs. This will be + * called by sysfs for whenever a show function is called by the user on a + * sysfs file associated with the kobjects we have registered. We need to + * transpose back from a "default" kobject to our custom struct foo_obj and + * then call the show function for that specific object. + */ +static ssize_t sysfs_fs_devices_attr_show(struct kobject *kobj, + struct attribute *attr, + char *buf) +{ + struct sysfs_fs_devices_attribute *attribute; + struct btrfs_sysfs_fs_devices *bs_fsd; + + attribute = container_of(attr, struct sysfs_fs_devices_attribute, attr); + bs_fsd = container_of(kobj, struct btrfs_sysfs_fs_devices, kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(bs_fsd, attribute, buf); +} + +/* + * Just like the default show function above, but this one is for when the + * sysfs "store" is requested (when a value is written to a file.) + */ +static ssize_t sysfs_fs_devices_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t len) +{ + struct sysfs_fs_devices_attribute *attribute; + struct btrfs_sysfs_fs_devices *bs_fsd; + + attribute = container_of(attr, struct sysfs_fs_devices_attribute, attr); + bs_fsd = container_of(kobj, struct btrfs_sysfs_fs_devices, kobj); + + if (!attribute->store) + return -EIO; + + return attribute->store(bs_fsd, attribute, buf, len); +} + +/* Our custom sysfs_ops that we will associate with our ktype later on */ +static const struct sysfs_ops sysfs_fs_devices_ops = { + .show = sysfs_fs_devices_attr_show, + .store = sysfs_fs_devices_attr_store, +}; + +/* + * The release function for our object. This is REQUIRED by the kernel to + * have. We free the memory held in our object here. + * + * NEVER try to get away with just a "blank" release function to try to be + * smarter than the kernel. Turns out, no one ever is... + */ +static void sysfs_fs_devices_release(struct kobject *kobj) +{ + struct btrfs_sysfs_fs_devices *bs_fsd; + + bs_fsd = container_of(kobj, struct btrfs_sysfs_fs_devices, kobj); + kfree(bs_fsd); +} + +#define GEN_SHOW_FUNC_SFD_EX(NAME, FIELD, FMT) \ +static ssize_t sfd_##NAME##_show( \ + struct btrfs_sysfs_fs_devices *bs_fsd, \ + struct sysfs_fs_devices_attribute *attr, \ + char *buf) \ +{ \ + if(!bs_fsd->fs_devices){ \ + strcpy(buf,"fail\n"); \ + return 5; \ + } \ + \ + return sprintf(buf,FMT,FIELD); \ +} \ + \ +static struct sysfs_fs_devices_attribute sfd_##NAME##_attribute = \ + __ATTR(NAME, 0666, sfd_##NAME##_show,0); + + +#define GEN_SHOW_FUNC_SFD(FIELD, FMT) \ + GEN_SHOW_FUNC_SFD_EX(FIELD, bs_fsd->fs_devices->FIELD, FMT) + +GEN_SHOW_FUNC_SFD(opened, "%d") +GEN_SHOW_FUNC_SFD(seeding, "%d") +GEN_SHOW_FUNC_SFD(latest_devid, "%llu") +GEN_SHOW_FUNC_SFD(latest_trans, "%llu") +GEN_SHOW_FUNC_SFD(open_devices, "%llu") +GEN_SHOW_FUNC_SFD(rw_devices, "%llu") +GEN_SHOW_FUNC_SFD(missing_devices, "%llu") +GEN_SHOW_FUNC_SFD(total_rw_bytes, "%llu") +GEN_SHOW_FUNC_SFD(num_can_discard, "%llu") + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *sysfs_fs_devices_attrs[] = { + + &sfd_opened_attribute.attr, + &sfd_seeding_attribute.attr, + &sfd_latest_devid_attribute.attr, + &sfd_latest_trans_attribute.attr, + &sfd_open_devices_attribute.attr, + &sfd_rw_devices_attribute.attr, + &sfd_total_rw_bytes_attribute.attr, + &sfd_missing_devices_attribute.attr, + &sfd_num_can_discard_attribute.attr, + + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct kobj_type btrfs_sysfs_fs_devices_ktype = { + .sysfs_ops = &sysfs_fs_devices_ops, + .release = sysfs_fs_devices_release, + .default_attrs = sysfs_fs_devices_attrs +}; + +static struct btrfs_sysfs_fs_devices * btrfs_sysfs_find_fs_devices( + struct btrfs_fs_devices *fs_devices){ + struct btrfs_sysfs_fs_devices *bs_fsd; + + list_for_each_entry(bs_fsd, &btrfs_sysfs_fs_devices_list, list){ + if( bs_fsd && fs_devices == bs_fsd->fs_devices ) + return bs_fsd; + } + + return NULL; +} + +/* register new fs_devices */ +static void btrfs_sysfs_add_fs_devices(struct btrfs_fs_devices *fs_devices){ + struct btrfs_sysfs_fs_devices *bs_fsd; + struct btrfs_sysfs_fsid *o_fsid; + + /* check if the device is already registered */ + if(btrfs_sysfs_find_fs_devices(fs_devices)) + return; + + o_fsid = add_fsid(fs_devices->fsid); + if(!o_fsid){ + printk("btrfs_sysfs: cannot add a fsid !!!\n"); + return; + } + + bs_fsd = kzalloc(sizeof(struct btrfs_sysfs_fs_devices),GFP_NOFS); + if( !bs_fsd ){ + printk("btrfs_sysfs: cannot register a fs_devices\n"); + return; + } + + printk("btrfs_sysfs: register fs_devices\n"); + if( kobject_init_and_add(&bs_fsd->kobj, + &btrfs_sysfs_fs_devices_ktype, + o_fsid->kobj, + "%s", "fs_devices") ){ + kobject_put(&bs_fsd->kobj); + printk("btrfs_sysfs: cannot add a fs_devices\n"); + kfree(bs_fsd); + return; + + } + + list_add(&bs_fsd->list, &btrfs_sysfs_fs_devices_list ); + bs_fsd->fs_devices = fs_devices; +} + +/* unregister a fs_devices */ +static void btrfs_sysfs_remove_fs_devices(struct btrfs_fs_devices *fs_devices){ + struct btrfs_sysfs_fs_devices *bs_fsd; + struct btrfs_sysfs_device *bs_dev; + + /* check if the fs_devices is still in use from another device */ + list_for_each_entry(bs_dev, &btrfs_sysfs_device_list, list){ + if(! bs_dev->dev ) continue; + if(bs_dev->dev->fs_devices == fs_devices) + return; + } + + while((bs_fsd = btrfs_sysfs_find_fs_devices(fs_devices)) != NULL ){ + bs_fsd->fs_devices = 0; + kobject_put(&bs_fsd->kobj); + list_del_init(&bs_fsd->list); + } + + del_fsid(fs_devices->fsid); +} + +/* + * ******************************************************************* + * Sysfs code related to the devices + * ******************************************************************* + */ + +struct sysfs_device_attribute { + struct attribute attr; + ssize_t (*show)(struct btrfs_sysfs_device *bs_dev, + struct sysfs_device_attribute *attr, char *buf); + ssize_t (*store)(struct btrfs_sysfs_device *bs_dev, + struct sysfs_device_attribute *attr, + const char *buf, size_t count); +}; + +/* + * The default show function that must be passed to sysfs. This will be + * called by sysfs for whenever a show function is called by the user on a + * sysfs file associated with the kobjects we have registered. We need to + * transpose back from a "default" kobject to our custom struct foo_obj and + * then call the show function for that specific object. + */ +static ssize_t sysfs_device_attr_show(struct kobject *kobj, + struct attribute *attr, + char *buf) +{ + struct sysfs_device_attribute *attribute; + struct btrfs_sysfs_device *bs_dev; + + attribute = container_of(attr, struct sysfs_device_attribute, attr); + bs_dev = container_of(kobj, struct btrfs_sysfs_device, kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(bs_dev, attribute, buf); +} + +/* + * Just like the default show function above, but this one is for when the + * sysfs "store" is requested (when a value is written to a file.) + */ +static ssize_t sysfs_device_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t len) +{ + struct sysfs_device_attribute *attribute; + struct btrfs_sysfs_device *bs_dev; + + attribute = container_of(attr, struct sysfs_device_attribute, attr); + bs_dev = container_of(kobj, struct btrfs_sysfs_device, kobj); + + if (!attribute->store) + return -EIO; + + return attribute->store(bs_dev, attribute, buf, len); +} + +/* Our custom sysfs_ops that we will associate with our ktype later on */ +static const struct sysfs_ops sysfs_device_ops = { + .show = sysfs_device_attr_show, + .store = sysfs_device_attr_store, +}; + +/* + * The release function for our object. This is REQUIRED by the kernel to + * have. We free the memory held in our object here. + * + * NEVER try to get away with just a "blank" release function to try to be + * smarter than the kernel. Turns out, no one ever is... + */ +static void sysfs_device_release(struct kobject *kobj) +{ + struct btrfs_sysfs_device *bs_dev; + + bs_dev = container_of(kobj, struct btrfs_sysfs_device, kobj); + kfree(bs_dev); +} + +#define GEN_SHOW_FUNC_SD_EX(NAME, CHECK, FIELD, FMT) \ +static ssize_t sd_##NAME##_show( \ + struct btrfs_sysfs_device *bs_dev, \ + struct sysfs_device_attribute *attr, \ + char *buf) \ +{ \ + if(!bs_dev->dev) \ + return 0; \ + \ + if(!(CHECK)) \ + return 0; \ + \ + return sprintf(buf,(FMT),(FIELD)); \ +} \ + \ +static struct sysfs_device_attribute sd_##NAME##_attribute = \ + __ATTR(NAME, 0666, sd_##NAME##_show,0); + + +#define GEN_SHOW_FUNC_SD(FIELD, FMT) \ + GEN_SHOW_FUNC_SD_EX(FIELD, 1, bs_dev->dev->FIELD, FMT) + +GEN_SHOW_FUNC_SD(generation, "%llu\n") +GEN_SHOW_FUNC_SD(devid, "%llu\n") +GEN_SHOW_FUNC_SD(missing, "%d\n") +GEN_SHOW_FUNC_SD(writeable, "%d\n") +GEN_SHOW_FUNC_SD(total_bytes, "%llu\n") +GEN_SHOW_FUNC_SD(disk_total_bytes, "%llu\n") +GEN_SHOW_FUNC_SD(bytes_used, "%llu\n") + +static ssize_t sd_fsid_show( + struct btrfs_sysfs_device *bs_dev, + struct sysfs_device_attribute *attr, + char *buf) +{ + if(!bs_dev->dev) + return 0; + + /* TODO: should we lock something ? */ + uuid_unparse(bs_dev->dev->fs_devices->fsid, buf); + buf[BTRFS_UUID_SIZE*2+4] = ''\n''; + return BTRFS_UUID_SIZE*2+4+1; +} + +static struct sysfs_device_attribute sd_fsid_attribute + __ATTR(fsid, 0666, sd_fsid_show,0); + +GEN_SHOW_FUNC_SD_EX(major, bs_dev->dev->bdev, + MAJOR(bs_dev->dev->bdev->bd_dev), "%u\n") +GEN_SHOW_FUNC_SD_EX(minor, bs_dev->dev->bdev, + MINOR(bs_dev->dev->bdev->bd_dev), "%u\n") +GEN_SHOW_FUNC_SD_EX(write_errors, + bs_dev->dev->dev_stats_valid, + btrfs_dev_stat_read(bs_dev->dev, BTRFS_DEV_STAT_WRITE_ERRS), + "%u\n") +GEN_SHOW_FUNC_SD_EX(read_errors, + bs_dev->dev->dev_stats_valid, + btrfs_dev_stat_read(bs_dev->dev, BTRFS_DEV_STAT_READ_ERRS), + "%u\n") +GEN_SHOW_FUNC_SD_EX(flush_errors, + bs_dev->dev->dev_stats_valid, + btrfs_dev_stat_read(bs_dev->dev, BTRFS_DEV_STAT_FLUSH_ERRS), + "%u\n") +GEN_SHOW_FUNC_SD_EX(corruption_errors, + bs_dev->dev->dev_stats_valid, + btrfs_dev_stat_read(bs_dev->dev, BTRFS_DEV_STAT_CORRUPTION_ERRS), + "%u\n") +GEN_SHOW_FUNC_SD_EX(generation_errors, + bs_dev->dev->dev_stats_valid, + btrfs_dev_stat_read(bs_dev->dev, BTRFS_DEV_STAT_GENERATION_ERRS), + "%u\n") + + +static ssize_t sd_name_show( + struct btrfs_sysfs_device *bs_dev, + struct sysfs_device_attribute *attr, + char *buf) +{ + int n; + + if(!bs_dev->dev) + return 0; + + rcu_read_lock(); + n = sprintf(buf, "%s\n", rcu_str_deref(bs_dev->dev->name)); + rcu_read_unlock(); + return n; +} + +static struct sysfs_device_attribute sd_name_attribute + __ATTR(name, 0666, sd_name_show,0); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *sysfs_device_attrs[] = { + &sd_generation_attribute.attr, + &sd_devid_attribute.attr, + &sd_writeable_attribute.attr, + &sd_missing_attribute.attr, + &sd_total_bytes_attribute.attr, + &sd_disk_total_bytes_attribute.attr, + &sd_bytes_used_attribute.attr, + &sd_fsid_attribute.attr, + &sd_name_attribute.attr, + &sd_major_attribute.attr, + &sd_minor_attribute.attr, + &sd_write_errors_attribute.attr, + &sd_read_errors_attribute.attr, + &sd_flush_errors_attribute.attr, + &sd_corruption_errors_attribute.attr, + &sd_generation_errors_attribute.attr, + + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct kobj_type btrfs_sysfs_device_ktype = { + .sysfs_ops = &sysfs_device_ops, + .release = sysfs_device_release, + .default_attrs = sysfs_device_attrs +}; + +static struct btrfs_sysfs_device * btrfs_sysfs_find_device( + struct btrfs_device *dev){ + struct btrfs_sysfs_device *bs_dev; + + list_for_each_entry(bs_dev, &btrfs_sysfs_device_list, list){ + if( dev == bs_dev->dev ) + return bs_dev; + } + + return NULL; +} + +void btrfs_sysfs_add_device(struct btrfs_device *dev){ + + struct btrfs_sysfs_device *bs_dev; + char uuid_buf[BTRFS_UUID_SIZE*2+5]; + + /* check if the device is already registered */ + if(btrfs_sysfs_find_device(dev)) + return; + + bs_dev = kzalloc(sizeof(struct btrfs_sysfs_device), GFP_NOFS); + if( !bs_dev ){ + printk("btrfs_sysfs: cannot register a device\n"); + return; + } + + uuid_unparse(dev->uuid, uuid_buf); + //bs_dev->kobj.kset = btrfs_devices_kset; + printk("btrfs_sysfs: register device ''%s''\n",uuid_buf); + if( kobject_init_and_add(&bs_dev->kobj, + &btrfs_sysfs_device_ktype, + btrfs_devices_kobj, + "%s",uuid_buf) ){ + //kobject_put(&bs_dev->kobj); + printk("btrfs_sysfs: cannot add a device\n"); + kfree(bs_dev); + return; + } + + btrfs_sysfs_add_fs_devices(dev->fs_devices); + + list_add(&bs_dev->list,&btrfs_sysfs_device_list ); + + bs_dev->dev = dev; + +} + +void btrfs_sysfs_remove_device(struct btrfs_device *dev){ + struct btrfs_sysfs_device *bs_dev; + + + while((bs_dev = btrfs_sysfs_find_device(dev)) != NULL ){ + bs_dev->dev = 0; + kobject_put(&bs_dev->kobj); + list_del_init(&bs_dev->list); + } + btrfs_sysfs_remove_fs_devices(dev->fs_devices); + +} + + +/* + * ******************************************************************* + * Sysfs code related to the filesystem + * ******************************************************************* + */ + +struct sysfs_filesystem_attribute { + struct attribute attr; + ssize_t (*show)(struct btrfs_sysfs_filesystem *bs_fs, + struct sysfs_filesystem_attribute *attr, char *buf); + ssize_t (*store)(struct btrfs_sysfs_filesystem *bs_fs, + struct sysfs_filesystem_attribute *attr, + const char *buf, size_t count); +}; + +/* + * The default show function that must be passed to sysfs. This will be + * called by sysfs for whenever a show function is called by the user on a + * sysfs file associated with the kobjects we have registered. We need to + * transpose back from a "default" kobject to our custom struct foo_obj and + * then call the show function for that specific object. + */ +static ssize_t sysfs_filesystem_attr_show(struct kobject *kobj, + struct attribute *attr, + char *buf) +{ + struct sysfs_filesystem_attribute *attribute; + struct btrfs_sysfs_filesystem *bs_fs; + + attribute = container_of(attr, struct sysfs_filesystem_attribute, attr); + bs_fs = container_of(kobj, struct btrfs_sysfs_filesystem, kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(bs_fs, attribute, buf); +} + +/* + * Just like the default show function above, but this one is for when the + * sysfs "store" is requested (when a value is written to a file.) + */ +static ssize_t sysfs_filesystem_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t len) +{ + struct sysfs_filesystem_attribute *attribute; + struct btrfs_sysfs_filesystem *bs_fs; + + attribute = container_of(attr, struct sysfs_filesystem_attribute, attr); + bs_fs = container_of(kobj, struct btrfs_sysfs_filesystem, kobj); + + if (!attribute->store) + return -EIO; + + return attribute->store(bs_fs, attribute, buf, len); +} + +/* Our custom sysfs_ops that we will associate with our ktype later on */ +static const struct sysfs_ops sysfs_filesystem_ops = { + .show = sysfs_filesystem_attr_show, + .store = sysfs_filesystem_attr_store, +}; + +/* + * The release function for our object. This is REQUIRED by the kernel to + * have. We free the memory held in our object here. + * + * NEVER try to get away with just a "blank" release function to try to be + * smarter than the kernel. Turns out, no one ever is... + */ +static void sysfs_filesystem_release(struct kobject *kobj) +{ + struct btrfs_sysfs_filesystem *bs_fs; + + bs_fs = container_of(kobj, struct btrfs_sysfs_filesystem, kobj); + kfree(bs_fs); +} + +#define GEN_SHOW_FUNC_SF_EX(NAME, FIELD, FMT) \ +static ssize_t sf_##NAME##_show( \ + struct btrfs_sysfs_filesystem *bs_fs, \ + struct sysfs_filesystem_attribute *attr, \ + char *buf) \ +{ \ + if(!bs_fs->fs_info){ \ + strcpy(buf,"fail\n"); \ + return 5; \ + } \ + \ + return sprintf(buf,FMT,FIELD); \ +} \ + \ +static struct sysfs_filesystem_attribute sf_##NAME##_attribute = \ + __ATTR(NAME, 0666, sf_##NAME##_show,0); + + +#define GEN_SHOW_FUNC_SF(FIELD, FMT) \ + GEN_SHOW_FUNC_SF_EX(FIELD, bs_fs->fs_info->FIELD, FMT) + + +static ssize_t sf_label_show( + struct btrfs_sysfs_filesystem *bs_fs, + struct sysfs_filesystem_attribute *attr, + char *buf) +{ + struct btrfs_super_block *bsb; + + if(!bs_fs->fs_info){ + strcpy(buf,"fail\n"); + return 5; + } + + bsb = bs_fs->fs_info->super_for_commit; + if(!bsb) + bsb = bs_fs->fs_info->super_copy; + if(bsb) + return sprintf(buf, "%s\n", bsb->label); + else + return 0; +} + +static struct sysfs_filesystem_attribute sf_label_attribute + __ATTR(label, 0666, sf_label_show,0); + +GEN_SHOW_FUNC_SF(generation, "%llu") +GEN_SHOW_FUNC_SF(last_trans_committed, "%llu") +GEN_SHOW_FUNC_SF(fs_state, "0x%016llx") +GEN_SHOW_FUNC_SF_EX(balance_running, + atomic_read(&bs_fs->fs_info->balance_running), "%d") +GEN_SHOW_FUNC_SF_EX(balance_pause_req, + atomic_read(&bs_fs->fs_info->balance_pause_req), "%d") +GEN_SHOW_FUNC_SF_EX(balance_cancel_req, + atomic_read(&bs_fs->fs_info->balance_cancel_req), "%d") +GEN_SHOW_FUNC_SF_EX(scrubs_running, + atomic_read(&bs_fs->fs_info->scrubs_running), "%d") +GEN_SHOW_FUNC_SF_EX(scrub_pause_req, + atomic_read(&bs_fs->fs_info->scrub_pause_req), "%d") +GEN_SHOW_FUNC_SF_EX(scrubs_paused, + atomic_read(&bs_fs->fs_info->scrubs_paused), "%d") +GEN_SHOW_FUNC_SF_EX(scrub_cancel_req, + atomic_read(&bs_fs->fs_info->scrub_cancel_req), "%d") + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *sysfs_filesystem_attrs[] = { + + &sf_label_attribute.attr, + &sf_generation_attribute.attr, + &sf_last_trans_committed_attribute.attr, + &sf_fs_state_attribute.attr, + &sf_balance_running_attribute.attr, + &sf_balance_pause_req_attribute.attr, + &sf_balance_cancel_req_attribute.attr, + &sf_scrubs_running_attribute.attr, + &sf_scrub_pause_req_attribute.attr, + &sf_scrubs_paused_attribute.attr, + &sf_scrub_cancel_req_attribute.attr, + + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct kobj_type btrfs_sysfs_filesystem_ktype = { + .sysfs_ops = &sysfs_filesystem_ops, + .release = sysfs_filesystem_release, + .default_attrs = sysfs_filesystem_attrs +}; + +static struct btrfs_sysfs_filesystem * btrfs_sysfs_find_filesystem( + struct btrfs_fs_info *fs_info){ + struct btrfs_sysfs_filesystem *bs_fs; + + list_for_each_entry(bs_fs, &btrfs_sysfs_filesystem_list, list){ + if( fs_info == bs_fs->fs_info ) + return bs_fs; + } + + return NULL; +} + +/* register new filesystem */ +void btrfs_sysfs_add_filesystem(struct btrfs_fs_info *fs_info){ + struct btrfs_sysfs_filesystem *bs_fs; + char uuid_buf[BTRFS_UUID_SIZE*2+5]; + struct btrfs_sysfs_fsid *o_fsid; + + /* check if the device is already registered */ + if(btrfs_sysfs_find_filesystem(fs_info)) + return; + + uuid_unparse(fs_info->fsid, uuid_buf); + o_fsid = find_fsid(fs_info->fsid); + if(!o_fsid ){ + printk("btrfs_sysfs: cannot find the fsid ''%s''\n", uuid_buf); + return; + } + + bs_fs = kzalloc(sizeof(struct btrfs_sysfs_filesystem),GFP_NOFS); + if( !bs_fs ){ + printk("btrfs_sysfs: cannot register a filesystem\n"); + return; + } + + //bs_fs->kobj.kset = btrfs_filesystems_kobj; + printk("btrfs_sysfs: register filesystem ''%s''\n",uuid_buf); + if( kobject_init_and_add(&bs_fs->kobj, + &btrfs_sysfs_filesystem_ktype, + o_fsid->kobj, + "%s","fs_info") ){ + //kobject_put(&bs_fs->kobj); + printk("btrfs_sysfs: cannot add a filesystem\n"); + kfree(bs_fs); + return; + + } + + list_add(&bs_fs->list,&btrfs_sysfs_filesystem_list ); + + bs_fs->fs_info = fs_info; +} + +/* unregister a filesystem */ +void btrfs_sysfs_remove_filesystem(struct btrfs_fs_info *fs_info){ + struct btrfs_sysfs_filesystem *bs_fs; + + while((bs_fs = btrfs_sysfs_find_filesystem(fs_info)) != NULL ){ + bs_fs->fs_info = 0; + kobject_put(&bs_fs->kobj); + list_del_init(&bs_fs->list); + } +} + +#else + +static struct kobject *btrfs_kobject=0; + void btrfs_exit_sysfs(void) { - kset_unregister(btrfs_kset); + if(btrfs_kobject) + kobject_put(btrfs_kobject); + btrfs_kobject=0; +} + + +int btrfs_init_sysfs(void) +{ + btrfs_kobject = kobject_create_and_add("btrfs", fs_kobj); + if( !btrfs_kobject ){ + btrfs_exit_sysfs( ); + return -ENOMEM; + } + return 0; } +void btrfs_sysfs_add_device(struct btrfs_device *dev){ } +void btrfs_sysfs_remove_device(struct btrfs_device *dev){ } +void btrfs_sysfs_add_filesystem(struct btrfs_fs_info *fs_info){ } +void btrfs_sysfs_remove_filesystem(struct btrfs_fs_info *fs_info){ } +#endif diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h new file mode 100644 index 0000000..92b6a9f --- /dev/null +++ b/fs/btrfs/sysfs.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 G.Baroncelli. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include "ctree.h" + +/* register new device */ +void btrfs_sysfs_add_device(struct btrfs_device *dev); + +/* unregister a device */ +void btrfs_sysfs_remove_device(struct btrfs_device *dev); + +/* register new filesystem */ +void btrfs_sysfs_add_filesystem(struct btrfs_fs_info *fs_info); + +/* unregister a filesystem */ +void btrfs_sysfs_remove_filesystem(struct btrfs_fs_info *fs_info); -- 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
Add the hook in the btrfs filesystem code to supporting sysfs. --- fs/btrfs/volumes.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index ecaad40..e1075ab 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -36,6 +36,7 @@ #include "async-thread.h" #include "check-integrity.h" #include "rcu-string.h" +#include "sysfs.h" static int init_first_rw_device(struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -405,6 +406,8 @@ static noinline int device_list_add(const char *path, } } + btrfs_sysfs_add_device(device); + if (found_transid > fs_devices->latest_trans) { fs_devices->latest_devid = devid; fs_devices->latest_trans = found_transid; @@ -458,6 +461,9 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig) INIT_LIST_HEAD(&device->dev_alloc_list); list_add(&device->dev_list, &fs_devices->devices); + + btrfs_sysfs_add_device(device); + device->fs_devices = fs_devices; fs_devices->num_devices++; } @@ -576,7 +582,7 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) new_device->in_fs_metadata = 0; new_device->can_discard = 0; list_replace_rcu(&device->dev_list, &new_device->dev_list); - + //btrfs_sysfs_remove_device(device); call_rcu(&device->rcu, free_device); } mutex_unlock(&fs_devices->device_list_mutex); @@ -1430,6 +1436,9 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) cur_devices = device->fs_devices; mutex_lock(&root->fs_info->fs_devices->device_list_mutex); + + btrfs_sysfs_remove_device(device); + list_del_rcu(&device->dev_list); device->fs_devices->num_devices--; @@ -1744,6 +1753,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) */ mutex_lock(&root->fs_info->fs_devices->device_list_mutex); list_add_rcu(&device->dev_list, &root->fs_info->fs_devices->devices); + btrfs_sysfs_add_device(device); list_add(&device->dev_alloc_list, &root->fs_info->fs_devices->alloc_list); root->fs_info->fs_devices->num_devices++; @@ -4306,6 +4316,9 @@ static struct btrfs_device *add_missing_dev(struct btrfs_root *root, spin_lock_init(&device->io_lock); INIT_LIST_HEAD(&device->dev_alloc_list); memcpy(device->uuid, dev_uuid, BTRFS_UUID_SIZE); + + btrfs_sysfs_add_device(device); + return device; } -- 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
Goffredo Baroncelli
2012-Aug-23 12:13 UTC
[PATCH v0 3/4] Add a new Kconfig section to enable or disable the sysfs.
Add support for sysfs to BTRFS. This patch add the Kconfig part to enable or disable the exporting of teh BTRFS information via sysfs. --- fs/btrfs/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index d33f01c..187e057 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -50,3 +50,12 @@ config BTRFS_FS_CHECK_INTEGRITY In most cases, unless you are a btrfs developer who needs to verify the integrity of (super)-block write requests during the run of a regression test, say N + +config BTRFS_FS_SYSFS + bool "Btrfs sysfs support" + depends on BTRFS_FS + help + This options allow to export some btrfs filesystems/devices + properties via the sysfs interface. + + -- 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
Goffredo Baroncelli
2012-Aug-23 12:13 UTC
[PATCH v0 4/4] Btrfs sysfs support documentation.
Update of the Documentation/filesystes/btrfs.txt file. --- 0 files changed diff --git a/Documentation/filesystems/btrfs.txt b/Documentation/filesystems/btrfs.txt index 7671352..35840ab 100644 --- a/Documentation/filesystems/btrfs.txt +++ b/Documentation/filesystems/btrfs.txt @@ -89,3 +89,78 @@ btrfsck: do a limited check of the FS extent trees. btrfs-debug-tree: print all of the FS metadata in text form. Example: btrfs-debug-tree /dev/sda2 >& big_output_file + + + + SYSFS SUPPORT + ============+ +If the option CONFIG_BTRFS_FS_SYSFS is enabled, BTRFS exports some +information via the sysfs filesystem. +Todate these information are read-only. + +The information exported are related to both the devices and the filesystems. +When a devices is registred (for example via the btrfs filesystem scan +command), BTRFS creates the following entries under /sys/fs/btrfs: + +/sys/fs/btrfs: + + - devices -> information per device basis + - filesystem -> information per filesystem basis + +/sys/fs/btrfs/devices/<device-UUID>: + + - bytes_used + - devid + - disk_total_bytes + - fsid -> fielsystem UUID + - generation + - major [*] + - minor [*] + - missing + - name -> device name (eg: /dev/sda) + - total_bytes + - write_errors -> error info + - corruption_errors -> error info + - read_errors -> error info + - generation_errors -> error info + - flush_errors -> error info + - writeable + +[*] These information are available only after the mounting of the +filesystem. + +/sys/fs/btrfs/filesystem/<filesystem-UUID>/fs_info: + + - balance_cancel_req -> balance status information + - balance_pause_req -> balance status information + - balance_running -> balance status information + - fs_state + - generation + - label -> label of the filesystem + - last_trans_committed + - scrub_cancel_req -> scrub status information + - scrub_pause_req -> scrub status information + - scrubs_paused -> scrub status information + - scrubs_running -> scrub status information + +/sys/fs/btrfs/filesystem/<filesystem-UUID>/fs_devices: + + - latest_devid + - latest_trans + - missing_devices + - num_can_discard + - open_devices + - opened + - rw_devices + - seeding + - total_rw_bytes + +The filesystems information are split in two section (fs_info and fs_devices) +because the fs_devices entries are available after the scan of the device(s), +but the fs_info entries are available only after the mount of the +filesystem. + +The link between the filesystem and the device(s) is the fsid == +filesystem-UUID. + -- 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
On Thu, 23 Aug 2012 14:12:59 +0200, Goffredo Baroncelli wrote:> Export via sysfs some information about the btrfs devices and > filesystem. > --- > fs/btrfs/super.c | 4 > fs/btrfs/sysfs.c | 933 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > fs/btrfs/sysfs.h | 31 ++ > 3 files changed, 963 insertions(+), 5 deletions(-) > create mode 100644 fs/btrfs/sysfs.hHi Goffredo, The Signed-off-by line is missing, it is mandatory. [...]> +struct btrfs_sysfs_fsid { > + struct kobject *kobj; > + struct list_head list; > + u8 fsid[BTRFS_FSID_SIZE]; > +};Tabsize needs to be 8.> +static struct list_head btrfs_sysfs_device_list; > +static struct list_head btrfs_sysfs_fs_devices_list; > +static struct list_head btrfs_sysfs_filesystem_list; > +static struct list_head btrfs_sysfs_fsid_list; > + > + > +void uuid_unparse( u8 *uuid, char *out ){ > + static char *i2x = "0123456789abcdef"; > + static int lengths[] = {4,2,2,2,6,0}; > + int i; > + > + for(i=0; ; i++){ > + int j; > + for(j=0; j < lengths[i] ; j++, uuid++){ > + *out++ = i2x[*uuid >> 4]; > + *out++ = i2x[*uuid & 0x0f]; > + } > + if( !lengths[i+1] ){ > + *out = 0; > + break; > + }else{ > + *out++ = ''-''; > + } > + } > +}All the missing spaces and all the spaces that are too much... One line contains a trailing TAB character before the end of the line. A newline is missing before the opening curly bracket of the function body. An empty line between the ''int j;'' and the code is common, like the one after the ''int i;''. [more lines deleted...] Please refer to: Documentation/SubmittingPatches Documentation/SubmitChecklist Documentation/CodingStyle And apply: scripts/checkpatch.pl I did not look at the patch itself after noticing the style issues. -- 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