diff mbox

[v0,1/4] Add support for sysfs to btrfs.

Message ID 20120823121242.12203.50365.stgit@venice.bhome (mailing list archive)
State New, archived
Headers show

Commit Message

Goffredo Baroncelli Aug. 23, 2012, 12:12 p.m. UTC
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


--
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

Comments

Stefan Behrens Aug. 23, 2012, 2:01 p.m. UTC | #1
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.h

Hi 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
diff mbox

Patch

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);