@@ -24,23 +24,951 @@
#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;
+static struct kobject *btrfs_devices_kobj;
+static struct kobject *btrfs_filesystems_kobj;
+
+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++ = '-';
+ }
+ }
+}
+
+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) {
+ pr_crit("btrfs_sysfs: cannot register the fsid '%s'\n", buf);
+ return NULL;
+ }
-/* /sys/fs/btrfs/ entry */
-static struct kset *btrfs_kset;
+ o_fsid->kobj = kobject_create_and_add(buf, btrfs_filesystems_kobj);
+ if (!o_fsid->kobj) {
+ pr_crit("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) {
+ pr_warn("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 = NULL;
+ btrfs_devices_kobj = NULL;
+ btrfs_filesystems_kobj = NULL;
+
+ 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 = NULL;
+ 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 = NULL;
+ 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 = NULL;
+ 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 = NULL;
+ btrfs_devices_kobj = NULL;
+ btrfs_kobject = NULL;
+
+}
+
+/*
+ * *******************************************************************
+ * 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) \
+ return 0; \
+ \
+ 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) {
+ pr_crit("btrfs_sysfs: cannot add a fsid !!!\n");
+ return;
+ }
+
+ bs_fsd = kzalloc(sizeof(struct btrfs_sysfs_fs_devices), GFP_NOFS);
+ if (!bs_fsd) {
+ pr_crit("btrfs_sysfs: cannot register a fs_devices\n");
+ return;
+ }
+
+ pr_info("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);
+ pr_crit("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) {
+ pr_crit("btrfs_sysfs: cannot register a device\n");
+ return;
+ }
+
+ uuid_unparse(dev->uuid, uuid_buf);
+ pr_info("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)) {
+ pr_crit("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) \
+ return 0; \
+ \
+ 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)
+ return 0;
+
+ 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) {
+ pr_crit("btrfs_sysfs: cannot find the fsid '%s'\n",
+ uuid_buf);
+ return;
+ }
+
+ bs_fs = kzalloc(sizeof(struct btrfs_sysfs_filesystem),
+ GFP_NOFS);
+ if (!bs_fs) {
+ pr_crit("btrfs_sysfs: cannot register a filesystem\n");
+ return;
+ }
+
+ pr_info("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")) {
+ pr_crit("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;
+
void btrfs_exit_sysfs(void)
{
- kset_unregister(btrfs_kset);
+ if (btrfs_kobject)
+ kobject_put(btrfs_kobject);
+ btrfs_kobject = NULL;
+}
+
+
+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
new file mode 100644
@@ -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);
Export via sysfs some information about the btrfs devices and filesystem. Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> --- fs/btrfs/sysfs.c | 938 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/sysfs.h | 31 ++ 2 files changed, 964 insertions(+), 5 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