* [RFC][PATCH 1 of 4] Configfs is really sysfs
@ 2005-08-30 22:54 Daniel Phillips
2005-08-30 22:57 ` [RFC][PATCH 2 " Daniel Phillips
2005-08-30 23:13 ` [RFC][PATCH 1 " Joel Becker
0 siblings, 2 replies; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 22:54 UTC (permalink / raw)
To: linux-kernel, Andrew Morton; +Cc: Joel Becker, Greg KH
Hi Andrew,
Configfs blithely ingests kobject.h and kobject.c into itself, just changing
the names. Furthermore, more than half of configfs is copied verbatim from
sysfs, the only difference being the name changes. After undoing the name
changes and adding a few new fields to kobject structures, configfs is able
to use the real thing instead of its own imitation.
The changes I made to kobject.h and sysfs.h are:
* add module owner to kobj_type.
* add group_operations to kobj_type (because configfs does it this way
not because it is right)
* add a children field to kset. This is likely the same as the blandly
named "list" field but I haven't confirmed it.
* add a default_groups field to kset, analogous to the default_attrs of
kobj_type. Hmm, somebody seems to be mixing up types and containers
here, but let's just close our eyes for now.
* add an s_links field to sysfs_dirent to support configfs's user
createable symlinks.
* add two new methods to sysfs_ops for fancy symlink hooks
* add a questionable release method to sysfs_ops. Sysfs and configfs
have slightly different notions of when to release objects, one of
them is probably wrong.
That's it, no new fields in kobjects themselves, and just three or four fields
in other allocateable structures. After these changes, no structures at all
are left in configfs.h. Configfs is now running happily using the kobject
machinery instead of its own mutated clones and unsurprisingly, sysfs still
runs happily too. These changes are all found in the first patch of this
series.
I then looked into exactly how configfs and sysfs are different. To reduce
the noise, I concatentated all the files in each directory into two single
files. With redundant declarations removed, configfs came in at 1897 lines
and sysfs at 1680. Diffing those two files shows:
diff -u fs/sysfs/sysfs.c fs/configfs/configfs.c | diffstat configfs.c | 1497
++++++++++++++++++++++++++++++++++---------------------------
1 files changed, 857 insertions(+), 640 deletions(-)
So we see that two thirds of sysfs made it into configfs unchanged. Of the
remaining one third that configfs has not copied, about one third supports
read/write/mmappable attribute files (why should configfs not have them
too?), a little less than a third involves needlessly importing its own
version of setattr, and the remainder, about 300 lines, exports the kernel
interface for manipulating the user-visible sysfs tree.
Allowing for a few lines of fluff, configfs's value add is about 750 lines
of user space glue for namespace operations. Nothing below that glue layer
is changed, except cosmetically. So configfs really is sysfs. By adding
about 300 lines to configfs we can add the vfs-bypass code, and voila,
configfs becomess sysfs. Another 200 lines gives us the binary blob
attributes as well. There is no reason whatsover for configfs and sysfs to
live on as separate code bases. If we really want to make a distinction, we
can make the distinction with a flag.
But it would be stupid to forbid users from creating directories in sysfs or
to forbid kernel modules from directly tweaking a configfs namespace. Why
should the kernel not be able to add objects to a directory a user created?
It should be up to the module author to decide these things.
Please do not push configfs to stable in this form. It is not actually a new
filesystem, it is an extension to sysfs. Merging it as is would add more
than a thousand lines of pointless kernel bloat. If indeed we wish to
present exactly the semantics configfs now offers, we do not need a separate
code base to do so.
The four patches in this patch set:
1) Add new fields to kobjects; update other headers to match
2) Sysfs all in one file
3) Configfs all in one file
4) A configfs kernel example using sysfs instead of configfs structures
Regards,
Daniel
diff -up --recursive 2.6.13-rc5-mm1.clean/include/linux/configfs.h 2.6.13-rc5-mm1/include/linux/configfs.h
--- 2.6.13-rc5-mm1.clean/include/linux/configfs.h 2005-08-09 18:23:31.000000000 -0400
+++ 2.6.13-rc5-mm1/include/linux/configfs.h 2005-08-29 18:30:41.000000000 -0400
@@ -46,120 +46,32 @@
#define CONFIGFS_ITEM_NAME_LEN 20
-struct module;
-
-struct configfs_item_operations;
-struct configfs_group_operations;
-struct configfs_attribute;
-struct configfs_subsystem;
-
-struct config_item {
- char *ci_name;
- char ci_namebuf[CONFIGFS_ITEM_NAME_LEN];
- struct kref ci_kref;
- struct list_head ci_entry;
- struct config_item *ci_parent;
- struct config_group *ci_group;
- struct config_item_type *ci_type;
- struct dentry *ci_dentry;
-};
-
-extern int config_item_set_name(struct config_item *, const char *, ...);
-
-static inline char *config_item_name(struct config_item * item)
-{
- return item->ci_name;
-}
-
-extern void config_item_init(struct config_item *);
-extern void config_item_init_type_name(struct config_item *item,
- const char *name,
- struct config_item_type *type);
-extern void config_item_cleanup(struct config_item *);
-
-extern struct config_item * config_item_get(struct config_item *);
-extern void config_item_put(struct config_item *);
-
-struct config_item_type {
- struct module *ct_owner;
- struct configfs_item_operations *ct_item_ops;
- struct configfs_group_operations *ct_group_ops;
- struct configfs_attribute **ct_attrs;
-};
-
+extern void kobject_init_type_name(struct kobject *item, const char *name, struct kobj_type *type);
/**
- * group - a group of config_items of a specific type, belonging
+ * group - a group of kobjects of a specific type, belonging
* to a specific subsystem.
*/
-struct config_group {
- struct config_item cg_item;
- struct list_head cg_children;
- struct configfs_subsystem *cg_subsys;
- struct config_group **default_groups;
-};
-
+extern void config_group_init(struct kset *group);
+extern void config_group_init_type_name(struct kset *group, const char *name, struct kobj_type *type);
-extern void config_group_init(struct config_group *group);
-extern void config_group_init_type_name(struct config_group *group,
- const char *name,
- struct config_item_type *type);
-
-
-static inline struct config_group *to_config_group(struct config_item *item)
+static inline struct kset *to_config_group(struct kobject *item)
{
- return item ? container_of(item,struct config_group,cg_item) : NULL;
+ return item ? container_of(item,struct kset,kobj) : NULL;
}
-static inline struct config_group *config_group_get(struct config_group *group)
+static inline struct kset *config_group_get(struct kset *group)
{
- return group ? to_config_group(config_item_get(&group->cg_item)) : NULL;
+ return group ? to_config_group(kobject_get(&group->kobj)) : NULL;
}
-static inline void config_group_put(struct config_group *group)
+static inline void config_group_put(struct kset *group)
{
- config_item_put(&group->cg_item);
+ kobject_put(&group->kobj);
}
-extern struct config_item *config_group_find_obj(struct config_group *, const char *);
-
-
-struct configfs_attribute {
- char *ca_name;
- struct module *ca_owner;
- mode_t ca_mode;
-};
-
-
-/*
- * If allow_link() exists, the item can symlink(2) out to other
- * items. If the item is a group, it may support mkdir(2).
- * Groups supply one of make_group() and make_item(). If the
- * group supports make_group(), one can create group children. If it
- * supports make_item(), one can create config_item children. If it has
- * default_groups on group->default_groups, it has automatically created
- * group children. default_groups may coexist alongsize make_group() or
- * make_item(), but if the group wishes to have only default_groups
- * children (disallowing mkdir(2)), it need not provide either function.
- * If the group has commit(), it supports pending and commited (active)
- * items.
- */
-struct configfs_item_operations {
- void (*release)(struct config_item *);
- ssize_t (*show_attribute)(struct config_item *, struct configfs_attribute *,char *);
- ssize_t (*store_attribute)(struct config_item *,struct configfs_attribute *,const char *, size_t);
- int (*allow_link)(struct config_item *src, struct config_item *target);
- int (*drop_link)(struct config_item *src, struct config_item *target);
-};
-
-struct configfs_group_operations {
- struct config_item *(*make_item)(struct config_group *group, const char *name);
- struct config_group *(*make_group)(struct config_group *group, const char *name);
- int (*commit_item)(struct config_item *item);
- void (*drop_item)(struct config_group *group, struct config_item *item);
-};
-
+extern struct kobject *config_group_find_obj(struct kset *, const char *);
/**
@@ -185,20 +97,13 @@ struct configfs_group_operations {
#endif
-struct configfs_subsystem {
- struct config_group su_group;
- struct semaphore su_sem;
-};
-
-static inline struct configfs_subsystem *to_configfs_subsystem(struct config_group *group)
+static inline struct subsystem *to_configfs_subsystem(struct kset *kset)
{
- return group ?
- container_of(group, struct configfs_subsystem, su_group) :
- NULL;
+ return kset ? container_of(kset, struct subsystem, kset) : NULL;
}
-int configfs_register_subsystem(struct configfs_subsystem *subsys);
-void configfs_unregister_subsystem(struct configfs_subsystem *subsys);
+int configfs_register_subsystem(struct subsystem *subsys);
+void configfs_unregister_subsystem(struct subsystem *subsys);
#endif /* __KERNEL__ */
diff -up --recursive 2.6.13-rc5-mm1.clean/include/linux/kobject.h 2.6.13-rc5-mm1/include/linux/kobject.h
--- 2.6.13-rc5-mm1.clean/include/linux/kobject.h 2005-08-09 18:23:13.000000000 -0400
+++ 2.6.13-rc5-mm1/include/linux/kobject.h 2005-08-29 02:40:27.000000000 -0400
@@ -69,7 +69,9 @@ extern char * kobject_get_path(struct ko
struct kobj_type {
void (*release)(struct kobject *);
+ struct module *ct_owner;
struct sysfs_ops * sysfs_ops;
+ struct configfs_group_operations *ct_group_ops;
struct attribute ** default_attrs;
};
@@ -106,6 +108,10 @@ struct kset {
spinlock_t list_lock;
struct kobject kobj;
struct kset_hotplug_ops * hotplug_ops;
+
+ /* configfs fields: merge us! */
+ struct list_head cg_children;
+ struct kset **default_groups;
};
@@ -144,7 +150,7 @@ extern struct kobject * kset_find_obj(st
* Use this when initializing an embedded kset with no other
* fields to initialize.
*/
-#define set_kset_name(str) .kset = { .kobj = { .name = str } }
+#define set_kset_name(str) .kset = { .kobj = { .k_name = str } }
@@ -156,7 +162,7 @@ struct subsystem {
#define decl_subsys(_name,_type,_hotplug_ops) \
struct subsystem _name##_subsys = { \
.kset = { \
- .kobj = { .name = __stringify(_name) }, \
+ .kobj = { .k_name = __stringify(_name) }, \
.ktype = _type, \
.hotplug_ops =_hotplug_ops, \
} \
@@ -164,7 +170,7 @@ struct subsystem _name##_subsys = { \
#define decl_subsys_name(_varname,_name,_type,_hotplug_ops) \
struct subsystem _varname##_subsys = { \
.kset = { \
- .kobj = { .name = __stringify(_name) }, \
+ .kobj = { .k_name = __stringify(_name) }, \
.ktype = _type, \
.hotplug_ops =_hotplug_ops, \
} \
diff -up --recursive 2.6.13-rc5-mm1.clean/include/linux/sysfs.h 2.6.13-rc5-mm1/include/linux/sysfs.h
--- 2.6.13-rc5-mm1.clean/include/linux/sysfs.h 2005-08-09 18:23:13.000000000 -0400
+++ 2.6.13-rc5-mm1/include/linux/sysfs.h 2005-08-29 16:58:02.000000000 -0400
@@ -12,6 +12,7 @@
#include <asm/atomic.h>
+struct kset;
struct kobject;
struct module;
@@ -63,12 +64,36 @@ struct bin_attribute {
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
+ int (*allow_link)(struct kobject *src, struct kobject *target);
+ int (*drop_link)(struct kobject *src, struct kobject *target);
+ void (*release)(struct kobject *);
+};
+
+/*
+ * If allow_link() exists, the item can symlink(2) out to other
+ * items. If the item is a group, it may support mkdir(2).
+ * Groups supply one of make_group() and make_item(). If the
+ * group supports make_group(), one can create group children. If it
+ * supports make_item(), one can create kobject children. If it has
+ * default_groups on group->default_groups, it has automatically created
+ * group children. default_groups may coexist alongsize make_group() or
+ * make_item(), but if the group wishes to have only default_groups
+ * children (disallowing mkdir(2)), it need not provide either function.
+ * If the group has commit(), it supports pending and commited (active)
+ * items.
+ */
+struct configfs_group_operations {
+ struct kobject *(*make_item)(struct kset *group, const char *name);
+ struct kset *(*make_group)(struct kset *group, const char *name);
+ int (*commit_item)(struct kobject *item);
+ void (*drop_item)(struct kset *group, struct kobject *item);
};
struct sysfs_dirent {
atomic_t s_count;
struct list_head s_sibling;
struct list_head s_children;
+ struct list_head s_links; /* configfs */
void * s_element;
int s_type;
umode_t s_mode;
diff -up --recursive 2.6.13-rc5-mm1.clean/lib/kobject.c 2.6.13-rc5-mm1/lib/kobject.c
--- 2.6.13-rc5-mm1.clean/lib/kobject.c 2005-08-09 18:23:13.000000000 -0400
+++ 2.6.13-rc5-mm1/lib/kobject.c 2005-08-29 21:29:24.000000000 -0400
@@ -344,7 +344,9 @@ void kobject_cleanup(struct kobject * ko
kfree(kobj->k_name);
kobj->k_name = NULL;
if (t && t->release)
- t->release(kobj);
+ t->release(kobj); /* which one of us... */
+ if (t && t->sysfs_ops && t->sysfs_ops->release)
+ t->sysfs_ops->release(kobj); /* ...is bogus? */
if (s)
kset_put(s);
if (parent)
^ permalink raw reply [flat|nested] 22+ messages in thread
* [RFC][PATCH 2 of 4] Configfs is really sysfs
2005-08-30 22:54 [RFC][PATCH 1 of 4] Configfs is really sysfs Daniel Phillips
@ 2005-08-30 22:57 ` Daniel Phillips
2005-08-30 22:59 ` [RFC][PATCH 3 " Daniel Phillips
2005-08-30 23:22 ` [RFC][PATCH 2 " Daniel Phillips
2005-08-30 23:13 ` [RFC][PATCH 1 " Joel Becker
1 sibling, 2 replies; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 22:57 UTC (permalink / raw)
To: linux-kernel; +Cc: Andrew Morton, Joel Becker, Greg KH
Sysfs rearranged as a single file for analysis purposes.
diff -up --recursive 2.6.13-rc5-mm1.clean/fs/sysfs/Makefile 2.6.13-rc5-mm1/fs/sysfs/Makefile
--- 2.6.13-rc5-mm1.clean/fs/sysfs/Makefile 2005-06-17 15:48:29.000000000 -0400
+++ 2.6.13-rc5-mm1/fs/sysfs/Makefile 2005-08-29 17:13:59.000000000 -0400
@@ -2,5 +2,4 @@
# Makefile for the sysfs virtual filesystem
#
-obj-y := inode.o file.o dir.o symlink.o mount.o bin.o \
- group.o
+obj-y := sysfs.o
diff -up --recursive 2.6.13-rc5-mm1.clean/fs/sysfs/sysfs.c 2.6.13-rc5-mm1/fs/sysfs/sysfs.c
--- 2.6.13-rc5-mm1.clean/fs/sysfs/sysfs.c 2005-08-30 17:52:35.000000000 -0400
+++ 2.6.13-rc5-mm1/fs/sysfs/sysfs.c 2005-08-29 21:04:40.000000000 -0400
@@ -0,0 +1,1680 @@
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/backing-dev.h>
+#include <linux/pagemap.h>
+#include <linux/fsnotify.h>
+
+struct sysfs_symlink {
+ char *link_name;
+ struct kobject *sl_target;
+};
+
+static inline struct kobject *to_kobj(struct dentry *dentry)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ return ((struct kobject *)sd->s_element);
+}
+
+static inline struct attribute *to_attr(struct dentry *dentry)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ return ((struct attribute *)sd->s_element);
+}
+
+static inline struct kobject *sysfs_get_kobject(struct dentry *dentry)
+{
+ struct kobject *kobj = NULL;
+
+ spin_lock(&dcache_lock);
+ if (!d_unhashed(dentry)) {
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ if (sd->s_type & SYSFS_KOBJ_LINK) {
+ struct sysfs_symlink *sl = sd->s_element;
+ kobj = kobject_get(sl->sl_target);
+ } else
+ kobj = kobject_get(sd->s_element);
+ }
+ spin_unlock(&dcache_lock);
+
+ return kobj;
+}
+
+static kmem_cache_t *sysfs_dir_cachep;
+
+static void release_sysfs_dirent(struct sysfs_dirent *sd)
+{
+ if (sd->s_type & SYSFS_KOBJ_LINK) {
+ struct sysfs_symlink *sl = sd->s_element;
+ kfree(sl->link_name);
+ kobject_put(sl->sl_target);
+ kfree(sl);
+ }
+ kfree(sd->s_iattr);
+ kmem_cache_free(sysfs_dir_cachep, sd);
+}
+
+static struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd)
+{
+ if (sd) {
+ WARN_ON(!atomic_read(&sd->s_count));
+ atomic_inc(&sd->s_count);
+ }
+ return sd;
+}
+
+static void sysfs_put(struct sysfs_dirent *sd)
+{
+ WARN_ON(!atomic_read(&sd->s_count));
+ if (atomic_dec_and_test(&sd->s_count))
+ release_sysfs_dirent(sd);
+}
+
+/*
+ * inode.c - basic inode and dentry operations.
+ */
+
+int sysfs_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ struct inode *inode = dentry->d_inode;
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ struct iattr *sd_iattr;
+ unsigned int ia_valid = iattr->ia_valid;
+ int error;
+
+ if (!sd)
+ return -EINVAL;
+
+ sd_iattr = sd->s_iattr;
+
+ error = inode_change_ok(inode, iattr);
+ if (error)
+ return error;
+
+ error = inode_setattr(inode, iattr);
+ if (error)
+ return error;
+
+ if (!sd_iattr) {
+ /* setting attributes for the first time, allocate now */
+ sd_iattr = kmalloc(sizeof(struct iattr), GFP_KERNEL);
+ if (!sd_iattr)
+ return -ENOMEM;
+ /* assign default attributes */
+ memset(sd_iattr, 0, sizeof(struct iattr));
+ sd_iattr->ia_mode = sd->s_mode;
+ sd_iattr->ia_uid = 0;
+ sd_iattr->ia_gid = 0;
+ sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime =
+ CURRENT_TIME;
+ sd->s_iattr = sd_iattr;
+ }
+
+ /* attributes were changed atleast once in past */
+
+ if (ia_valid & ATTR_UID)
+ sd_iattr->ia_uid = iattr->ia_uid;
+ if (ia_valid & ATTR_GID)
+ sd_iattr->ia_gid = iattr->ia_gid;
+ if (ia_valid & ATTR_ATIME)
+ sd_iattr->ia_atime = timespec_trunc(iattr->ia_atime,
+ inode->i_sb->s_time_gran);
+ if (ia_valid & ATTR_MTIME)
+ sd_iattr->ia_mtime = timespec_trunc(iattr->ia_mtime,
+ inode->i_sb->s_time_gran);
+ if (ia_valid & ATTR_CTIME)
+ sd_iattr->ia_ctime = timespec_trunc(iattr->ia_ctime,
+ inode->i_sb->s_time_gran);
+ if (ia_valid & ATTR_MODE) {
+ umode_t mode = iattr->ia_mode;
+
+ if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
+ mode &= ~S_ISGID;
+ sd_iattr->ia_mode = sd->s_mode = mode;
+ }
+
+ return error;
+}
+
+static struct inode_operations sysfs_inode_operations = {
+ .setattr = sysfs_setattr,
+};
+
+static struct super_block *sysfs_sb;
+
+static struct address_space_operations sysfs_aops = {
+ .readpage = simple_readpage,
+ .prepare_write = simple_prepare_write,
+ .commit_write = simple_commit_write
+};
+
+static struct backing_dev_info sysfs_backing_dev_info = {
+ .ra_pages = 0, /* No readahead */
+ .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
+};
+
+static struct inode *sysfs_new_inode(mode_t mode, struct sysfs_dirent *sd)
+{
+ struct inode *inode = new_inode(sysfs_sb);
+ if (inode) {
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_blocks = 0;
+ inode->i_mapping->a_ops = &sysfs_aops;
+ inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
+ inode->i_op = &sysfs_inode_operations;
+
+ if (sd->s_iattr) {
+ /* sysfs_dirent has non-default attributes
+ * get them for the new inode from persistent copy
+ * in sysfs_dirent */
+ struct iattr *iattr = sd->s_iattr;
+ inode->i_mode = iattr->ia_mode;
+ inode->i_uid = iattr->ia_uid;
+ inode->i_gid = iattr->ia_gid;
+ inode->i_atime = iattr->ia_atime;
+ inode->i_mtime = iattr->ia_mtime;
+ inode->i_ctime = iattr->ia_ctime;
+ } else {
+ inode->i_mode = mode;
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ }
+ }
+ return inode;
+}
+
+static int sysfs_create(struct dentry *dentry, int mode, int (*init) (struct inode *))
+{
+ int error = 0;
+ struct inode *inode = NULL;
+ if (dentry) {
+ if (!dentry->d_inode) {
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ if ((inode = sysfs_new_inode(mode, sd))) {
+ if (dentry->d_parent
+ && dentry->d_parent->d_inode) {
+ struct inode *p_inode =
+ dentry->d_parent->d_inode;
+ p_inode->i_mtime = p_inode->i_ctime =
+ CURRENT_TIME;
+ }
+ goto Proceed;
+ } else
+ error = -ENOMEM;
+ } else
+ error = -EEXIST;
+ } else
+ error = -ENOENT;
+ goto Done;
+
+ Proceed:
+ if (init)
+ error = init(inode);
+ if (!error) {
+ d_instantiate(dentry, inode);
+ if (S_ISDIR(mode)) /* pin only directory dentry in core */
+ dget(dentry);
+ } else
+ iput(inode);
+ Done:
+ return error;
+}
+
+/*
+ * Get the name for corresponding element represented by the given sysfs_dirent
+ */
+static const unsigned char *sysfs_get_name(struct sysfs_dirent *sd)
+{
+ struct attribute *attr;
+ struct bin_attribute *bin_attr;
+ struct sysfs_symlink *sl;
+
+ if (!sd || !sd->s_element)
+ BUG();
+
+ switch (sd->s_type) {
+ case SYSFS_DIR:
+ /* Always have a dentry so use that */
+ return sd->s_dentry->d_name.name;
+
+ case SYSFS_KOBJ_ATTR:
+ attr = sd->s_element;
+ return attr->name;
+
+ case SYSFS_KOBJ_BIN_ATTR:
+ bin_attr = sd->s_element;
+ return bin_attr->attr.name;
+
+ case SYSFS_KOBJ_LINK:
+ sl = sd->s_element;
+ return sl->link_name;
+ }
+ return NULL;
+}
+
+/*
+ * Unhashes the dentry corresponding to given sysfs_dirent
+ * Called with parent inode's i_sem held.
+ */
+static void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent)
+{
+ struct dentry *dentry = sd->s_dentry;
+
+ if (dentry) {
+ spin_lock(&dcache_lock);
+ spin_lock(&dentry->d_lock);
+ if (!(d_unhashed(dentry) && dentry->d_inode)) {
+ dget_locked(dentry);
+ __d_drop(dentry);
+ spin_unlock(&dentry->d_lock);
+ spin_unlock(&dcache_lock);
+ simple_unlink(parent->d_inode, dentry);
+ } else {
+ spin_unlock(&dentry->d_lock);
+ spin_unlock(&dcache_lock);
+ }
+ }
+}
+
+static void sysfs_hash_and_remove(struct dentry *dir, const char *name)
+{
+ struct sysfs_dirent *sd;
+ struct sysfs_dirent *parent_sd = dir->d_fsdata;
+
+ down(&dir->d_inode->i_sem);
+ list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+ if (!sd->s_element)
+ continue;
+ if (!strcmp(sysfs_get_name(sd), name)) {
+ list_del_init(&sd->s_sibling);
+ sysfs_drop_dentry(sd, dir);
+ sysfs_put(sd);
+ break;
+ }
+ }
+ up(&dir->d_inode->i_sem);
+}
+
+/*
+ * symlink.c - operations for sysfs symlinks.
+ */
+
+static int object_depth(struct kobject *kobj)
+{
+ struct kobject *p = kobj;
+ int depth = 0;
+ do {
+ depth++;
+ } while ((p = p->parent));
+ return depth;
+}
+
+static int object_path_length(struct kobject *kobj)
+{
+ struct kobject *p = kobj;
+ int length = 1;
+ do {
+ length += strlen(kobject_name(p)) + 1;
+ p = p->parent;
+ } while (p);
+ return length;
+}
+
+static void fill_object_path(struct kobject *kobj, char *buffer, int length)
+{
+ struct kobject *p;
+
+ --length;
+ for (p = kobj; p; p = p->parent) {
+ int cur = strlen(kobject_name(p));
+
+ /* back up enough to print this bus id with '/' */
+ length -= cur;
+ strncpy(buffer + length, kobject_name(p), cur);
+ *(buffer + --length) = '/';
+ }
+}
+
+static int sysfs_get_target_path(struct kobject *kobj, struct kobject *target, char *path)
+{
+ char *s;
+ int depth, size;
+
+ depth = object_depth(kobj);
+ size = object_path_length(target) + depth * 3 - 1;
+ if (size > PATH_MAX)
+ return -ENAMETOOLONG;
+
+ pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size);
+
+ for (s = path; depth--; s += 3)
+ strcpy(s, "../");
+
+ fill_object_path(target, path, size);
+ pr_debug("%s: path = '%s'\n", __FUNCTION__, path);
+
+ return 0;
+}
+
+DECLARE_RWSEM(sysfs_rename_sem);
+
+static int sysfs_getlink(struct dentry *dentry, char *path)
+{
+ struct kobject *kobj, *sl_target;
+ int error = 0;
+
+ kobj = sysfs_get_kobject(dentry->d_parent);
+ if (!kobj)
+ return -EINVAL;
+
+ sl_target = sysfs_get_kobject(dentry);
+ if (!sl_target) {
+ kobject_put(kobj);
+ return -EINVAL;
+ }
+
+ down_read(&sysfs_rename_sem);
+ error = sysfs_get_target_path(kobj, sl_target, path);
+ up_read(&sysfs_rename_sem);
+
+ kobject_put(kobj);
+ kobject_put(sl_target);
+ return error;
+
+}
+
+static int sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ int error = -ENOMEM;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ if (page)
+ error = sysfs_getlink(dentry, (char *)page);
+ nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
+ return 0;
+}
+
+static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *page = nd_get_link(nd);
+ if (!IS_ERR(page))
+ free_page((unsigned long)page);
+}
+
+static struct inode_operations sysfs_symlink_inode_operations = {
+ .readlink = generic_readlink,
+ .follow_link = sysfs_follow_link,
+ .put_link = sysfs_put_link,
+};
+
+static int init_symlink(struct inode *inode)
+{
+ inode->i_op = &sysfs_symlink_inode_operations;
+ return 0;
+}
+
+/*
+ * file.c - operations for regular (text) files.
+ */
+
+#define to_subsys(k) container_of(k, struct subsystem, kset.kobj)
+#define to_sattr(a) container_of(a, struct subsys_attribute, attr)
+
+/*
+ * Subsystem file operations.
+ * These operations allow subsystems to have files that can be
+ * read/written.
+ */
+static ssize_t subsys_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *page)
+{
+ struct subsystem *s = to_subsys(kobj);
+ struct subsys_attribute *sattr = to_sattr(attr);
+ ssize_t ret = -EIO;
+
+ if (sattr->show)
+ ret = sattr->show(s, page);
+ return ret;
+}
+
+static ssize_t subsys_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *page, size_t count)
+{
+ struct subsystem *s = to_subsys(kobj);
+ struct subsys_attribute *sattr = to_sattr(attr);
+ ssize_t ret = -EIO;
+
+ if (sattr->store)
+ ret = sattr->store(s, page, count);
+ return ret;
+}
+
+static struct sysfs_ops subsys_sysfs_ops = {
+ .show = subsys_attr_show,
+ .store = subsys_attr_store,
+};
+
+/*
+ * file.c - operations for regular (text) files.
+ */
+
+struct sysfs_buffer {
+ size_t count;
+ loff_t pos;
+ char *page;
+ struct sysfs_ops *ops;
+ struct semaphore sem;
+ int needs_read_fill;
+};
+
+/*
+ * Allocate @buffer->page, if it hasn't been already, then call the
+ * kobject's show() method to fill the buffer with this attribute's
+ * data. This is called only once, on the file's first read.
+ */
+static int fill_read_buffer(struct dentry *dentry, struct sysfs_buffer *buffer)
+{
+ struct attribute *attr = to_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+ struct sysfs_ops *ops = buffer->ops;
+ int ret = 0;
+ ssize_t count;
+
+ if (!buffer->page)
+ buffer->page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!buffer->page)
+ return -ENOMEM;
+
+ count = ops->show(kobj, attr, buffer->page);
+ buffer->needs_read_fill = 0;
+ BUG_ON(count > (ssize_t) PAGE_SIZE);
+ if (count >= 0)
+ buffer->count = count;
+ else
+ ret = count;
+ return ret;
+}
+
+/*
+ * Copy the buffer we filled in fill_read_buffer() to userspace.
+ * This is done at the reader's leisure, copying and advancing
+ * the amount they specify each time.
+ * This may be called continuously until the buffer is empty.
+ */
+static int flush_read_buffer(struct sysfs_buffer *buffer, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int error;
+
+ if (*ppos > buffer->count)
+ return 0;
+
+ if (count > (buffer->count - *ppos))
+ count = buffer->count - *ppos;
+
+ error = copy_to_user(buf, buffer->page + *ppos, count);
+ if (!error)
+ *ppos += count;
+ return error ? -EFAULT : count;
+}
+
+/*
+ * Allocate @buffer->page if it hasn't been already, then
+ * copy the user-supplied buffer into it.
+ */
+static int fill_write_buffer(struct sysfs_buffer *buffer, const char __user *buf,
+ size_t count)
+{
+ int error;
+
+ if (!buffer->page)
+ buffer->page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!buffer->page)
+ return -ENOMEM;
+
+ if (count > PAGE_SIZE)
+ count = PAGE_SIZE;
+ error = copy_from_user(buffer->page, buf, count);
+ buffer->needs_read_fill = 1;
+ return error ? -EFAULT : count;
+}
+
+/*
+ * Get the correct pointers for the kobject and the attribute we're
+ * dealing with, then call the store method for the attribute,
+ * passing the buffer that we acquired in fill_write_buffer().
+ */
+static int flush_write_buffer(struct dentry *dentry, struct sysfs_buffer *buffer,
+ size_t count)
+{
+ struct attribute *attr = to_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+ struct sysfs_ops *ops = buffer->ops;
+
+ return ops->store(kobj, attr, buffer->page, count);
+}
+
+static int check_perm(struct inode *inode, struct file *file)
+{
+ struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent);
+ struct attribute *attr = to_attr(file->f_dentry);
+ struct sysfs_buffer *buffer;
+ struct sysfs_ops *ops = NULL;
+ int error = 0;
+
+ if (!kobj || !attr)
+ goto Einval;
+
+ /* Grab the module reference for this attribute if we have one */
+ if (!try_module_get(attr->owner)) {
+ error = -ENODEV;
+ goto Done;
+ }
+
+ /* if the kobject has no ktype, then we assume that it is a subsystem
+ * itself, and use ops for it.
+ */
+ if (kobj->kset && kobj->kset->ktype)
+ ops = kobj->kset->ktype->sysfs_ops;
+ else if (kobj->ktype)
+ ops = kobj->ktype->sysfs_ops;
+ else
+ ops = &subsys_sysfs_ops;
+
+ /* No sysfs operations, either from having no subsystem,
+ * or the subsystem have no operations.
+ */
+ if (!ops)
+ goto Eaccess;
+
+ /* File needs write support.
+ * The inode's perms must say it's ok, and we must have a store method.
+ */
+ if (file->f_mode & FMODE_WRITE) {
+
+ if (!(inode->i_mode & S_IWUGO) || !ops->store)
+ goto Eaccess;
+
+ }
+
+ /* File needs read support.
+ * The inode's perms must say it's ok, and we there
+ * must be a show method for it.
+ */
+ if (file->f_mode & FMODE_READ) {
+ if (!(inode->i_mode & S_IRUGO) || !ops->show)
+ goto Eaccess;
+ }
+
+ /* No error? Great, allocate a buffer for the file, and store it
+ * it in file->private_data for easy access.
+ */
+ buffer = kmalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);
+ if (buffer) {
+ memset(buffer, 0, sizeof(struct sysfs_buffer));
+ init_MUTEX(&buffer->sem);
+ buffer->needs_read_fill = 1;
+ buffer->ops = ops;
+ file->private_data = buffer;
+ } else
+ error = -ENOMEM;
+ goto Done;
+
+ Einval:
+ error = -EINVAL;
+ goto Done;
+ Eaccess:
+ error = -EACCES;
+ module_put(attr->owner);
+ Done:
+ if (error && kobj)
+ kobject_put(kobj);
+ return error;
+}
+
+/*
+ * Userspace wants to read an attribute file. The attribute descriptor
+ * is in the file's ->d_fsdata. The target object is in the directory's
+ * ->d_fsdata.
+ *
+ * We call fill_read_buffer() to allocate and fill the buffer from the
+ * object's show() method exactly once (if the read is happening from
+ * the beginning of the file). That should fill the entire buffer with
+ * all the data the object has to offer for that attribute.
+ * We then call flush_read_buffer() to copy the buffer to userspace
+ * in the increments specified.
+ */
+static ssize_t sysfs_read_file(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct sysfs_buffer *buffer = file->private_data;
+ ssize_t retval = 0;
+
+ down(&buffer->sem);
+ if (buffer->needs_read_fill) {
+ if ((retval = fill_read_buffer(file->f_dentry, buffer)))
+ goto out;
+ }
+ pr_debug("%s: count = %d, ppos = %lld, buf = %s\n",
+ __FUNCTION__, count, *ppos, buffer->page);
+ retval = flush_read_buffer(buffer, buf, count, ppos);
+ out:
+ up(&buffer->sem);
+ return retval;
+}
+
+/*
+ * Similar to sysfs_read_file(), though working in the opposite direction.
+ * We allocate and fill the data from the user in fill_write_buffer(),
+ * then push it to the kobject in flush_write_buffer().
+ * There is no easy way for us to know if userspace is only doing a partial
+ * write, so we don't support them. We expect the entire buffer to come
+ * on the first write.
+ * Hint: if you're writing a value, first read the file, modify only the
+ * the value you're changing, then write entire buffer back.
+ */
+static ssize_t sysfs_write_file(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct sysfs_buffer *buffer = file->private_data;
+
+ down(&buffer->sem);
+ count = fill_write_buffer(buffer, buf, count);
+ if (count > 0)
+ count = flush_write_buffer(file->f_dentry, buffer, count);
+ if (count > 0)
+ *ppos += count;
+ up(&buffer->sem);
+ return count;
+}
+
+static int sysfs_open_file(struct inode *inode, struct file *filp)
+{
+ return check_perm(inode, filp);
+}
+
+static int sysfs_release(struct inode *inode, struct file *filp)
+{
+ struct kobject *kobj = to_kobj(filp->f_dentry->d_parent);
+ struct attribute *attr = to_attr(filp->f_dentry);
+ struct module *owner = attr->owner;
+ struct sysfs_buffer *buffer = filp->private_data;
+
+ if (kobj)
+ kobject_put(kobj);
+ /* After this point, attr should not be accessed. */
+ module_put(owner);
+
+ if (buffer) {
+ if (buffer->page)
+ free_page((unsigned long)buffer->page);
+ kfree(buffer);
+ }
+ return 0;
+}
+
+static struct file_operations sysfs_file_operations = {
+ .read = sysfs_read_file,
+ .write = sysfs_write_file,
+ .llseek = generic_file_llseek,
+ .open = sysfs_open_file,
+ .release = sysfs_release,
+};
+
+static int init_file(struct inode *inode)
+{
+ inode->i_size = PAGE_SIZE;
+ inode->i_fop = &sysfs_file_operations;
+ return 0;
+}
+
+static void sysfs_d_iput(struct dentry *dentry, struct inode *inode)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+
+ if (sd) {
+ BUG_ON(sd->s_dentry != dentry);
+ sd->s_dentry = NULL;
+ sysfs_put(sd);
+ }
+ iput(inode);
+}
+
+static struct dentry_operations sysfs_dentry_ops = {
+ .d_iput = sysfs_d_iput,
+};
+
+/*
+ * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent
+ */
+static struct sysfs_dirent *sysfs_new_dirent(struct sysfs_dirent *parent_sd,
+ void *element)
+{
+ struct sysfs_dirent *sd;
+
+ sd = kmem_cache_alloc(sysfs_dir_cachep, GFP_KERNEL);
+ if (!sd)
+ return NULL;
+
+ memset(sd, 0, sizeof(*sd));
+ atomic_set(&sd->s_count, 1);
+ INIT_LIST_HEAD(&sd->s_children);
+ list_add(&sd->s_sibling, &parent_sd->s_children);
+ sd->s_element = element;
+
+ return sd;
+}
+
+int sysfs_make_dirent(struct sysfs_dirent *parent_sd, struct dentry *dentry,
+ void *element, umode_t mode, int type)
+{
+ struct sysfs_dirent *sd;
+
+ sd = sysfs_new_dirent(parent_sd, element);
+ if (!sd)
+ return -ENOMEM;
+
+ sd->s_mode = mode;
+ sd->s_type = type;
+ sd->s_dentry = dentry;
+ if (dentry) {
+ dentry->d_fsdata = sysfs_get(sd);
+ dentry->d_op = &sysfs_dentry_ops;
+ }
+
+ return 0;
+}
+
+static int sysfs_add_file(struct dentry *dir, const struct attribute *attr, int type)
+{
+ struct sysfs_dirent *parent_sd = dir->d_fsdata;
+ umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG;
+ int error = 0;
+
+ down(&dir->d_inode->i_sem);
+ error = sysfs_make_dirent(parent_sd, NULL, (void *)attr, mode, type);
+ up(&dir->d_inode->i_sem);
+
+ return error;
+}
+
+int sysfs_create_file(struct kobject *kobj, const struct attribute *attr)
+{
+ BUG_ON(!kobj || !kobj->dentry || !attr);
+
+ return sysfs_add_file(kobj->dentry, attr, SYSFS_KOBJ_ATTR);
+}
+
+static int sysfs_add_link(struct dentry *parent, const char *name, struct kobject *target)
+{
+ struct sysfs_dirent *parent_sd = parent->d_fsdata;
+ struct sysfs_symlink *sl;
+ int error = 0;
+
+ error = -ENOMEM;
+ sl = kmalloc(sizeof(*sl), GFP_KERNEL);
+ if (!sl)
+ goto exit1;
+
+ sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL);
+ if (!sl->link_name)
+ goto exit2;
+
+ strcpy(sl->link_name, name);
+ sl->sl_target = kobject_get(target);
+
+ error = sysfs_make_dirent(parent_sd, NULL, sl, S_IFLNK | S_IRWXUGO,
+ SYSFS_KOBJ_LINK);
+ if (!error)
+ return 0;
+
+ kfree(sl->link_name);
+ exit2:
+ kfree(sl);
+ exit1:
+ return error;
+}
+
+/*
+ * dir.c - Operations for directories.
+ */
+
+static int sysfs_dir_open(struct inode *inode, struct file *file)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct sysfs_dirent *parent_sd = dentry->d_fsdata;
+
+ down(&dentry->d_inode->i_sem);
+ file->private_data = sysfs_new_dirent(parent_sd, NULL);
+ up(&dentry->d_inode->i_sem);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static int sysfs_dir_close(struct inode *inode, struct file *file)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct sysfs_dirent *cursor = file->private_data;
+
+ down(&dentry->d_inode->i_sem);
+ list_del_init(&cursor->s_sibling);
+ up(&dentry->d_inode->i_sem);
+
+ release_sysfs_dirent(cursor);
+
+ return 0;
+}
+
+/* Relationship between s_mode and the DT_xxx types */
+static inline unsigned char dt_type(struct sysfs_dirent *sd)
+{
+ return (sd->s_mode >> 12) & 15;
+}
+
+static int sysfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct sysfs_dirent *parent_sd = dentry->d_fsdata;
+ struct sysfs_dirent *cursor = filp->private_data;
+ struct list_head *p, *q = &cursor->s_sibling;
+ ino_t ino;
+ int i = filp->f_pos;
+
+ switch (i) {
+ case 0:
+ ino = dentry->d_inode->i_ino;
+ if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
+ break;
+ filp->f_pos++;
+ i++;
+ /* fallthrough */
+ case 1:
+ ino = parent_ino(dentry);
+ if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
+ break;
+ filp->f_pos++;
+ i++;
+ /* fallthrough */
+ default:
+ if (filp->f_pos == 2) {
+ list_del(q);
+ list_add(q, &parent_sd->s_children);
+ }
+ for (p = q->next; p != &parent_sd->s_children; p = p->next) {
+ struct sysfs_dirent *next;
+ const char *name;
+ int len;
+
+ next = list_entry(p, struct sysfs_dirent, s_sibling);
+ if (!next->s_element)
+ continue;
+
+ name = sysfs_get_name(next);
+ len = strlen(name);
+ if (next->s_dentry)
+ ino = next->s_dentry->d_inode->i_ino;
+ else
+ ino = iunique(sysfs_sb, 2);
+
+ if (filldir(dirent, name, len, filp->f_pos, ino,
+ dt_type(next)) < 0)
+ return 0;
+
+ list_del(q);
+ list_add(q, p);
+ p = q;
+ filp->f_pos++;
+ }
+ }
+ return 0;
+}
+
+static loff_t sysfs_dir_lseek(struct file *file, loff_t offset, int origin)
+{
+ struct dentry *dentry = file->f_dentry;
+
+ down(&dentry->d_inode->i_sem);
+ switch (origin) {
+ case 1:
+ offset += file->f_pos;
+ case 0:
+ if (offset >= 0)
+ break;
+ default:
+ up(&file->f_dentry->d_inode->i_sem);
+ return -EINVAL;
+ }
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ if (file->f_pos >= 2) {
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ struct sysfs_dirent *cursor = file->private_data;
+ struct list_head *p;
+ loff_t n = file->f_pos - 2;
+
+ list_del(&cursor->s_sibling);
+ p = sd->s_children.next;
+ while (n && p != &sd->s_children) {
+ struct sysfs_dirent *next;
+ next = list_entry(p, struct sysfs_dirent,
+ s_sibling);
+ if (next->s_element)
+ n--;
+ p = p->next;
+ }
+ list_add_tail(&cursor->s_sibling, p);
+ }
+ }
+ up(&dentry->d_inode->i_sem);
+ return offset;
+}
+
+static struct file_operations sysfs_dir_operations = {
+ .open = sysfs_dir_open,
+ .release = sysfs_dir_close,
+ .llseek = sysfs_dir_lseek,
+ .read = generic_read_dir,
+ .readdir = sysfs_readdir,
+};
+
+/*
+ * bin.c - binary file operations for sysfs.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Matthew Wilcox
+ * Copyright (c) 2004 Silicon Graphics, Inc.
+ */
+
+static inline struct bin_attribute *to_bin_attr(struct dentry *dentry)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ return ((struct bin_attribute *)sd->s_element);
+}
+
+static int fill_read(struct dentry *dentry, char *buffer, loff_t off,
+ size_t count)
+{
+ struct bin_attribute *attr = to_bin_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+
+ if (!attr->read)
+ return -EIO;
+
+ return attr->read(kobj, buffer, off, count);
+}
+
+static ssize_t read(struct file *file, char __user *userbuf, size_t count, loff_t *off)
+{
+ char *buffer = file->private_data;
+ struct dentry *dentry = file->f_dentry;
+ int size = dentry->d_inode->i_size;
+ loff_t offs = *off;
+ int ret;
+
+ if (count > PAGE_SIZE)
+ count = PAGE_SIZE;
+
+ if (size) {
+ if (offs > size)
+ return 0;
+ if (offs + count > size)
+ count = size - offs;
+ }
+
+ ret = fill_read(dentry, buffer, offs, count);
+ if (ret < 0)
+ return ret;
+ count = ret;
+
+ if (copy_to_user(userbuf, buffer, count))
+ return -EFAULT;
+
+ pr_debug("offs = %lld, *off = %lld, count = %zd\n", offs, *off, count);
+
+ *off = offs + count;
+
+ return count;
+}
+
+static int flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
+{
+ struct bin_attribute *attr = to_bin_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+
+ if (!attr->write)
+ return -EIO;
+
+ return attr->write(kobj, buffer, offset, count);
+}
+
+static ssize_t write(struct file *file, const char __user *userbuf, size_t count, loff_t *off)
+{
+ char *buffer = file->private_data;
+ struct dentry *dentry = file->f_dentry;
+ int size = dentry->d_inode->i_size;
+ loff_t offs = *off;
+
+ if (count > PAGE_SIZE)
+ count = PAGE_SIZE;
+ if (size) {
+ if (offs > size)
+ return 0;
+ if (offs + count > size)
+ count = size - offs;
+ }
+
+ if (copy_from_user(buffer, userbuf, count))
+ return -EFAULT;
+
+ count = flush_write(dentry, buffer, offs, count);
+ if (count > 0)
+ *off = offs + count;
+ return count;
+}
+
+static int mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct bin_attribute *attr = to_bin_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+
+ if (!attr->mmap)
+ return -EINVAL;
+
+ return attr->mmap(kobj, attr, vma);
+}
+
+static int open(struct inode *inode, struct file *file)
+{
+ struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent);
+ struct bin_attribute *attr = to_bin_attr(file->f_dentry);
+ int error = -EINVAL;
+
+ if (!kobj || !attr)
+ goto Done;
+
+ /* Grab the module reference for this attribute if we have one */
+ error = -ENODEV;
+ if (!try_module_get(attr->attr.owner))
+ goto Done;
+
+ error = -EACCES;
+ if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap))
+ goto Error;
+ if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap))
+ goto Error;
+
+ error = -ENOMEM;
+ file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!file->private_data)
+ goto Error;
+
+ error = 0;
+ goto Done;
+
+ Error:
+ module_put(attr->attr.owner);
+ Done:
+ if (error && kobj)
+ kobject_put(kobj);
+ return error;
+}
+
+static int release(struct inode *inode, struct file *file)
+{
+ struct kobject *kobj = to_kobj(file->f_dentry->d_parent);
+ struct bin_attribute *attr = to_bin_attr(file->f_dentry);
+ u8 *buffer = file->private_data;
+
+ if (kobj)
+ kobject_put(kobj);
+ module_put(attr->attr.owner);
+ kfree(buffer);
+ return 0;
+}
+
+struct file_operations bin_fops = {
+ .read = read,
+ .write = write,
+ .mmap = mmap,
+ .llseek = generic_file_llseek,
+ .open = open,
+ .release = release,
+};
+
+int sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr)
+{
+ BUG_ON(!kobj || !kobj->dentry || !attr);
+
+ return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
+}
+
+int sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr)
+{
+ sysfs_hash_and_remove(kobj->dentry, attr->attr.name);
+ return 0;
+}
+
+/* attaches attribute's sysfs_dirent to the dentry corresponding to the
+ * attribute file
+ */
+static int sysfs_attach_attr(struct sysfs_dirent *sd, struct dentry *dentry)
+{
+ struct attribute *attr = NULL;
+ struct bin_attribute *bin_attr = NULL;
+ int (*init) (struct inode *) = NULL;
+ int error = 0;
+
+ if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) {
+ bin_attr = sd->s_element;
+ attr = &bin_attr->attr;
+ } else {
+ attr = sd->s_element;
+ init = init_file;
+ }
+
+ dentry->d_fsdata = sysfs_get(sd);
+ sd->s_dentry = dentry;
+ error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init);
+ if (error) {
+ sysfs_put(sd);
+ return error;
+ }
+
+ if (bin_attr) {
+ dentry->d_inode->i_size = bin_attr->size;
+ dentry->d_inode->i_fop = &bin_fops;
+ }
+ dentry->d_op = &sysfs_dentry_ops;
+ d_rehash(dentry);
+
+ return 0;
+}
+
+static int sysfs_attach_link(struct sysfs_dirent *sd, struct dentry *dentry)
+{
+ int err = 0;
+
+ dentry->d_fsdata = sysfs_get(sd);
+ sd->s_dentry = dentry;
+ err = sysfs_create(dentry, S_IFLNK | S_IRWXUGO, init_symlink);
+ if (!err) {
+ dentry->d_op = &sysfs_dentry_ops;
+ d_rehash(dentry);
+ } else
+ sysfs_put(sd);
+
+ return err;
+}
+
+static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;
+ struct sysfs_dirent *sd;
+ int err = 0;
+
+ list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ const unsigned char *name = sysfs_get_name(sd);
+
+ if (strcmp(name, dentry->d_name.name))
+ continue;
+
+ if (sd->s_type & SYSFS_KOBJ_LINK)
+ err = sysfs_attach_link(sd, dentry);
+ else
+ err = sysfs_attach_attr(sd, dentry);
+ break;
+ }
+ }
+
+ return ERR_PTR(err);
+}
+
+struct inode_operations sysfs_dir_inode_operations = {
+ .lookup = sysfs_lookup,
+ .setattr = sysfs_setattr,
+};
+
+static int init_dir(struct inode *inode)
+{
+ inode->i_op = &sysfs_dir_inode_operations;
+ inode->i_fop = &sysfs_dir_operations;
+
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inode->i_nlink++;
+ return 0;
+}
+
+static int create_dir(struct kobject *k, struct dentry *p, const char *n, struct dentry **d)
+{
+ int error;
+ umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
+
+ down(&p->d_inode->i_sem);
+ *d = lookup_one_len(n, p, strlen(n));
+ if (!IS_ERR(*d)) {
+ error = sysfs_make_dirent(p->d_fsdata, *d, k, mode, SYSFS_DIR);
+ if (!error) {
+ error = sysfs_create(*d, mode, init_dir);
+ if (!error) {
+ p->d_inode->i_nlink++;
+ (*d)->d_op = &sysfs_dentry_ops;
+ d_rehash(*d);
+ }
+ }
+ if (error && (error != -EEXIST)) {
+ sysfs_put((*d)->d_fsdata);
+ d_drop(*d);
+ }
+ dput(*d);
+ } else
+ error = PTR_ERR(*d);
+ up(&p->d_inode->i_sem);
+ return error;
+}
+
+int sysfs_create_subdir(struct kobject *k, const char *n, struct dentry **d)
+{
+ return create_dir(k, k->dentry, n, d);
+}
+
+struct vfsmount *sysfs_mount;
+
+int sysfs_create_dir(struct kobject *kobj)
+{
+ struct dentry *dentry = NULL;
+ struct dentry *parent;
+ int error = 0;
+
+ BUG_ON(!kobj);
+
+ if (kobj->parent)
+ parent = kobj->parent->dentry;
+ else if (sysfs_mount && sysfs_mount->mnt_sb)
+ parent = sysfs_mount->mnt_sb->s_root;
+ else
+ return -EFAULT;
+
+ error = create_dir(kobj, parent, kobject_name(kobj), &dentry);
+ if (!error)
+ kobj->dentry = dentry;
+ return error;
+}
+
+static void remove_dir(struct dentry *d)
+{
+ struct dentry *parent = dget(d->d_parent);
+ struct sysfs_dirent *sd;
+
+ down(&parent->d_inode->i_sem);
+ d_delete(d);
+ sd = d->d_fsdata;
+ list_del_init(&sd->s_sibling);
+ sysfs_put(sd);
+ if (d->d_inode)
+ simple_rmdir(parent->d_inode, d);
+
+ pr_debug(" o %s removing done (%d)\n", d->d_name.name, atomic_read(&d->d_count));
+
+ up(&parent->d_inode->i_sem);
+ dput(parent);
+}
+
+void sysfs_remove_subdir(struct dentry *d)
+{
+ remove_dir(d);
+}
+
+/*
+ * sysfs_remove_dir - remove a kobject's directory.
+ * @kobj: object.
+ *
+ * The only thing special about this is that we remove any files in
+ * the directory before we remove the directory.
+ */
+void sysfs_remove_dir(struct kobject *kobj)
+{
+ struct dentry *dentry = dget(kobj->dentry);
+ struct sysfs_dirent *parent_sd;
+ struct sysfs_dirent *sd, *tmp;
+
+ if (!dentry)
+ return;
+
+ pr_debug("sysfs %s: removing dir\n", dentry->d_name.name);
+ down(&dentry->d_inode->i_sem);
+ parent_sd = dentry->d_fsdata;
+ list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
+ if (!sd->s_element || !(sd->s_type & SYSFS_NOT_PINNED))
+ continue;
+ list_del_init(&sd->s_sibling);
+ sysfs_drop_dentry(sd, dentry);
+ sysfs_put(sd);
+ }
+ up(&dentry->d_inode->i_sem);
+
+ remove_dir(dentry);
+ /* Drop reference from dget() on entrance. */
+ dput(dentry);
+}
+
+int sysfs_rename_dir(struct kobject *kobj, const char *new_name)
+{
+ int error = 0;
+ struct dentry *new_dentry, *parent;
+
+ if (!strcmp(kobject_name(kobj), new_name))
+ return -EINVAL;
+
+ if (!kobj->parent)
+ return -EINVAL;
+
+ down_write(&sysfs_rename_sem);
+ parent = kobj->parent->dentry;
+
+ down(&parent->d_inode->i_sem);
+
+ new_dentry = lookup_one_len(new_name, parent, strlen(new_name));
+ if (!IS_ERR(new_dentry)) {
+ if (!new_dentry->d_inode) {
+ error = kobject_set_name(kobj, "%s", new_name);
+ if (!error) {
+ d_add(new_dentry, NULL);
+ d_move(kobj->dentry, new_dentry);
+ } else
+ d_drop(new_dentry);
+ } else
+ error = -EEXIST;
+ dput(new_dentry);
+ }
+ up(&parent->d_inode->i_sem);
+ up_write(&sysfs_rename_sem);
+
+ return error;
+}
+
+/**
+ * sysfs_update_file - update the modified timestamp on an object attribute.
+ * @kobj: object we're acting for.
+ * @attr: attribute descriptor.
+ */
+int sysfs_update_file(struct kobject *kobj, const struct attribute *attr)
+{
+ struct dentry *dir = kobj->dentry;
+ struct dentry *victim;
+ int res = -ENOENT;
+
+ down(&dir->d_inode->i_sem);
+ victim = lookup_one_len(attr->name, dir, strlen(attr->name));
+ if (!IS_ERR(victim)) {
+ /* make sure dentry is really there */
+ if (victim->d_inode &&
+ (victim->d_parent->d_inode == dir->d_inode)) {
+ victim->d_inode->i_mtime = CURRENT_TIME;
+ fsnotify_modify(victim);
+
+ /**
+ * Drop reference from initial sysfs_get_dentry().
+ */
+ dput(victim);
+ res = 0;
+ } else
+ d_drop(victim);
+
+ /**
+ * Drop the reference acquired from sysfs_get_dentry() above.
+ */
+ dput(victim);
+ }
+ up(&dir->d_inode->i_sem);
+
+ return res;
+}
+
+/**
+ * sysfs_chmod_file - update the modified mode value on an object attribute.
+ * @kobj: object we're acting for.
+ * @attr: attribute descriptor.
+ * @mode: file permissions.
+ *
+ */
+int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode)
+{
+ struct dentry *dir = kobj->dentry;
+ struct dentry *victim;
+ struct inode *inode;
+ struct iattr newattrs;
+ int res = -ENOENT;
+
+ down(&dir->d_inode->i_sem);
+ victim = lookup_one_len(attr->name, dir, strlen(attr->name));
+ if (!IS_ERR(victim)) {
+ if (victim->d_inode &&
+ (victim->d_parent->d_inode == dir->d_inode)) {
+ inode = victim->d_inode;
+ down(&inode->i_sem);
+ newattrs.ia_mode = (mode & S_IALLUGO) |
+ (inode->i_mode & ~S_IALLUGO);
+ newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+ res = notify_change(victim, &newattrs);
+ up(&inode->i_sem);
+ }
+ dput(victim);
+ }
+ up(&dir->d_inode->i_sem);
+
+ return res;
+}
+
+/**
+ * sysfs_remove_file - remove an object attribute.
+ * @kobj: object we're acting for.
+ * @attr: attribute descriptor.
+ *
+ * Hash the attribute name and kill the victim.
+ */
+void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr)
+{
+ sysfs_hash_and_remove(kobj->dentry, attr->name);
+}
+
+/*
+ * fs/sysfs/group.c - Operations for adding/removing multiple files at once.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ *
+ * This file is released undert the GPL v2.
+ *
+ */
+static void remove_files(struct dentry *dir, const struct attribute_group *grp)
+{
+ struct attribute *const *attr;
+
+ for (attr = grp->attrs; *attr; attr++)
+ sysfs_hash_and_remove(dir, (*attr)->name);
+}
+
+static int create_files(struct dentry *dir, const struct attribute_group *grp)
+{
+ struct attribute *const *attr;
+ int error = 0;
+
+ for (attr = grp->attrs; *attr && !error; attr++) {
+ error = sysfs_add_file(dir, *attr, SYSFS_KOBJ_ATTR);
+ }
+ if (error)
+ remove_files(dir, grp);
+ return error;
+}
+
+int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
+{
+ struct dentry *dir;
+ int error;
+
+ BUG_ON(!kobj || !kobj->dentry);
+
+ if (grp->name) {
+ error = sysfs_create_subdir(kobj, grp->name, &dir);
+ if (error)
+ return error;
+ } else
+ dir = kobj->dentry;
+ dir = dget(dir);
+ if ((error = create_files(dir, grp))) {
+ if (grp->name)
+ sysfs_remove_subdir(dir);
+ }
+ dput(dir);
+ return error;
+}
+
+void sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp)
+{
+ struct dentry *dir;
+
+ if (grp->name)
+ dir = lookup_one_len(grp->name, kobj->dentry,
+ strlen(grp->name));
+ else
+ dir = dget(kobj->dentry);
+
+ remove_files(dir, grp);
+ if (grp->name)
+ sysfs_remove_subdir(dir);
+ /* release the ref. taken in this routine */
+ dput(dir);
+}
+
+/*
+ * mount.c - operations for initializing and mounting
+ */
+
+#define SYSFS_MAGIC 0x62656572
+
+static struct super_operations sysfs_ops = {
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
+};
+
+static struct sysfs_dirent sysfs_root = {
+ .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling),
+ .s_children = LIST_HEAD_INIT(sysfs_root.s_children),
+ .s_type = SYSFS_ROOT,
+};
+
+static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct inode *inode;
+ struct dentry *root;
+
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = SYSFS_MAGIC;
+ sb->s_op = &sysfs_ops;
+ sb->s_time_gran = 1;
+ sysfs_sb = sb;
+
+ inode = sysfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, &sysfs_root);
+ if (inode) {
+ inode->i_op = &sysfs_dir_inode_operations;
+ inode->i_fop = &sysfs_dir_operations;
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inode->i_nlink++;
+ } else {
+ pr_debug("sysfs: could not get root inode\n");
+ return -ENOMEM;
+ }
+
+ root = d_alloc_root(inode);
+ if (!root) {
+ pr_debug("%s: could not get root dentry!\n", __FUNCTION__);
+ iput(inode);
+ return -ENOMEM;
+ }
+ root->d_fsdata = &sysfs_root;
+ sb->s_root = root;
+ return 0;
+}
+
+static struct super_block *sysfs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return get_sb_single(fs_type, flags, data, sysfs_fill_super);
+}
+
+static struct file_system_type sysfs_fs_type = {
+ .name = "sysfs",
+ .get_sb = sysfs_get_sb,
+ .kill_sb = kill_litter_super,
+};
+
+int __init sysfs_init(void)
+{
+ int err = -ENOMEM;
+
+ sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",
+ sizeof(struct sysfs_dirent), 0, 0, NULL, NULL);
+ if (!sysfs_dir_cachep)
+ goto out;
+
+ err = register_filesystem(&sysfs_fs_type);
+ if (err)
+ goto out_err;
+ sysfs_mount = kern_mount(&sysfs_fs_type);
+ if (IS_ERR(sysfs_mount)) {
+ printk(KERN_ERR "sysfs: could not mount!\n");
+ err = PTR_ERR(sysfs_mount);
+ sysfs_mount = NULL;
+ unregister_filesystem(&sysfs_fs_type);
+ goto out_err;
+ }
+ out:
+ return err;
+ out_err:
+ kmem_cache_destroy(sysfs_dir_cachep);
+ sysfs_dir_cachep = NULL;
+ goto out;
+}
+
+/*
+ * sysfs_create_link - create symlink between two objects.
+ * @kobj: object whose directory we're creating the link in.
+ * @target: object we're pointing to.
+ * @name: name of the symlink.
+ */
+int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name)
+{
+ struct dentry *dentry = kobj->dentry;
+ int error = 0;
+
+ BUG_ON(!kobj || !kobj->dentry || !name);
+
+ down(&dentry->d_inode->i_sem);
+ error = sysfs_add_link(dentry, name, target);
+ up(&dentry->d_inode->i_sem);
+ return error;
+}
+
+void sysfs_remove_link(struct kobject *kobj, const char *name)
+{
+ sysfs_hash_and_remove(kobj->dentry, name);
+}
+
+EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
+EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
+EXPORT_SYMBOL_GPL(sysfs_create_dir);
+EXPORT_SYMBOL_GPL(sysfs_remove_dir);
+EXPORT_SYMBOL_GPL(sysfs_rename_dir);
+EXPORT_SYMBOL_GPL(sysfs_chmod_file);
+EXPORT_SYMBOL_GPL(sysfs_create_file);
+EXPORT_SYMBOL_GPL(sysfs_remove_file);
+EXPORT_SYMBOL_GPL(sysfs_update_file);
+EXPORT_SYMBOL_GPL(sysfs_create_group);
+EXPORT_SYMBOL_GPL(sysfs_remove_group);
+EXPORT_SYMBOL_GPL(sysfs_create_link);
+EXPORT_SYMBOL_GPL(sysfs_remove_link);
^ permalink raw reply [flat|nested] 22+ messages in thread
* [RFC][PATCH 3 of 4] Configfs is really sysfs
2005-08-30 22:57 ` [RFC][PATCH 2 " Daniel Phillips
@ 2005-08-30 22:59 ` Daniel Phillips
2005-08-30 23:03 ` [RFC][PATCH 4 " Daniel Phillips
` (2 more replies)
2005-08-30 23:22 ` [RFC][PATCH 2 " Daniel Phillips
1 sibling, 3 replies; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 22:59 UTC (permalink / raw)
To: linux-kernel; +Cc: Andrew Morton, Joel Becker, Greg KH
Configfs rewritten as a single file and updated to use kobjects instead of its
own clone of kobjects (config_items).
diff -up --recursive 2.6.13-rc5-mm1.clean/fs/configfs/Makefile 2.6.13-rc5-mm1/fs/configfs/Makefile
--- 2.6.13-rc5-mm1.clean/fs/configfs/Makefile 2005-08-09 18:23:30.000000000 -0400
+++ 2.6.13-rc5-mm1/fs/configfs/Makefile 2005-08-29 17:26:02.000000000 -0400
@@ -2,6 +2,5 @@
# Makefile for the configfs virtual filesystem
#
-obj-$(CONFIG_CONFIGFS_FS) += configfs.o
+obj-$(CONFIG_CONFIGFS_FS) += configfs.o ddbond.config.o
-configfs-objs := inode.o file.o dir.o symlink.o mount.o item.o
diff -up --recursive 2.6.13-rc5-mm1.clean/fs/configfs/configfs.c 2.6.13-rc5-mm1/fs/configfs/configfs.c
--- 2.6.13-rc5-mm1.clean/fs/configfs/configfs.c 2005-08-30 17:50:30.000000000 -0400
+++ 2.6.13-rc5-mm1/fs/configfs/configfs.c 2005-08-29 21:36:47.000000000 -0400
@@ -0,0 +1,1897 @@
+/*
+ * Based on sysfs:
+ * sysfs Copyright (C) 2001, 2002, 2003 Patrick Mochel
+ *
+ * configfs Copyright (C) 2005 Oracle. All rights reserved.
+ */
+
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/backing-dev.h>
+#include <linux/pagemap.h>
+#include <linux/configfs.h>
+
+#define CONFIGFS_ROOT 0x0001
+#define CONFIGFS_DIR 0x0002
+#define CONFIGFS_ITEM_ATTR 0x0004
+#define CONFIGFS_ITEM_LINK 0x0020
+#define CONFIGFS_USET_DIR 0x0040
+#define CONFIGFS_USET_DEFAULT 0x0080
+#define CONFIGFS_USET_DROPPING 0x0100
+#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR)
+
+struct sysfs_symlink {
+ struct list_head sl_list;
+ struct kobject *sl_target;
+};
+
+static inline struct kobject *to_kobj(struct dentry *dentry)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ return ((struct kobject *)sd->s_element);
+}
+
+static inline struct attribute *to_attr(struct dentry *dentry)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ return ((struct attribute *)sd->s_element);
+}
+
+static inline struct kobject *sysfs_get_kobject(struct dentry *dentry)
+{
+ struct kobject *kobj = NULL;
+
+ spin_lock(&dcache_lock);
+ if (!d_unhashed(dentry)) {
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ if (sd->s_type & CONFIGFS_ITEM_LINK) {
+ struct sysfs_symlink *sl = sd->s_element;
+ kobj = kobject_get(sl->sl_target);
+ } else
+ kobj = kobject_get(sd->s_element);
+ }
+ spin_unlock(&dcache_lock);
+
+ return kobj;
+}
+
+static kmem_cache_t *sysfs_dir_cachep;
+
+static void release_sysfs_dirent(struct sysfs_dirent *sd)
+{
+ if ((sd->s_type & CONFIGFS_ROOT))
+ return;
+ kmem_cache_free(sysfs_dir_cachep, sd);
+}
+
+static struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd)
+{
+ if (sd) {
+ WARN_ON(!atomic_read(&sd->s_count));
+ atomic_inc(&sd->s_count);
+ }
+ return sd;
+}
+
+static void sysfs_put(struct sysfs_dirent *sd)
+{
+ WARN_ON(!atomic_read(&sd->s_count));
+ if (atomic_dec_and_test(&sd->s_count))
+ release_sysfs_dirent(sd);
+}
+
+/*
+ * inode.c - basic inode and dentry operations.
+ */
+
+static struct super_block *sysfs_sb;
+
+static struct address_space_operations sysfs_aops = {
+ .readpage = simple_readpage,
+ .prepare_write = simple_prepare_write,
+ .commit_write = simple_commit_write
+};
+
+static struct backing_dev_info sysfs_backing_dev_info = {
+ .ra_pages = 0, /* No readahead */
+ .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
+};
+
+static struct inode *sysfs_new_inode(mode_t mode)
+{
+ struct inode *inode = new_inode(sysfs_sb);
+ if (inode) {
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_blocks = 0;
+ inode->i_mode = mode;
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_mapping->a_ops = &sysfs_aops;
+ inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
+ }
+ return inode;
+}
+
+static int sysfs_create(struct dentry *dentry, int mode, int (*init) (struct inode *))
+{
+ int error = 0;
+ struct inode *inode = NULL;
+ if (dentry) {
+ if (!dentry->d_inode) {
+ if ((inode = sysfs_new_inode(mode))) {
+ if (dentry->d_parent
+ && dentry->d_parent->d_inode) {
+ struct inode *p_inode =
+ dentry->d_parent->d_inode;
+ p_inode->i_mtime = p_inode->i_ctime =
+ CURRENT_TIME;
+ }
+ goto Proceed;
+ } else
+ error = -ENOMEM;
+ } else
+ error = -EEXIST;
+ } else
+ error = -ENOENT;
+ goto Done;
+
+ Proceed:
+ if (init)
+ error = init(inode);
+ if (!error) {
+ d_instantiate(dentry, inode);
+ if (S_ISDIR(mode) || S_ISLNK(mode)) /* pin link and directory dentries */
+ dget(dentry);
+ } else
+ iput(inode);
+ Done:
+ return error;
+}
+
+/*
+ * Get the name for corresponding element represented by the given sysfs_dirent
+ */
+static const unsigned char *sysfs_get_name(struct sysfs_dirent *sd)
+{
+ if (!sd || !sd->s_element)
+ BUG();
+
+ /* These always have a dentry, so use that */
+ if (sd->s_type & (CONFIGFS_DIR | CONFIGFS_ITEM_LINK))
+ return sd->s_dentry->d_name.name;
+
+ if (sd->s_type & CONFIGFS_ITEM_ATTR)
+ return ((struct attribute *)sd->s_element)->name;
+ return NULL;
+}
+
+/*
+ * Unhashes the dentry corresponding to given sysfs_dirent
+ * Called with parent inode's i_sem held.
+ */
+static void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent)
+{
+ struct dentry *dentry = sd->s_dentry;
+
+ if (dentry) {
+ spin_lock(&dcache_lock);
+ if (!(d_unhashed(dentry) && dentry->d_inode)) {
+ dget_locked(dentry);
+ __d_drop(dentry);
+ spin_unlock(&dcache_lock);
+ simple_unlink(parent->d_inode, dentry);
+ } else {
+ spin_unlock(&dcache_lock);
+ }
+ }
+}
+
+#if 0
+static void sysfs_hash_and_remove(struct dentry *dir, const char *name)
+{
+ struct sysfs_dirent *sd;
+ struct sysfs_dirent *parent_sd = dir->d_fsdata;
+
+ down(&dir->d_inode->i_sem);
+ list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+ if (!sd->s_element)
+ continue;
+ if (!strcmp(sysfs_get_name(sd), name)) {
+ list_del_init(&sd->s_sibling);
+ sysfs_drop_dentry(sd, dir);
+ sysfs_put(sd);
+ break;
+ }
+ }
+ up(&dir->d_inode->i_sem);
+}
+#endif
+
+static struct kset sysfs_root_group = {
+ .kobj = {
+ .name = "root",
+ .k_name = sysfs_root_group.kobj.name,
+ },
+};
+
+static int is_root(struct kobject *kobj)
+{
+ return kobj == &sysfs_root_group.kobj;
+}
+
+/*
+ * symlink.c - operations for sysfs symlinks.
+ */
+
+static int object_depth(struct kobject *kobj)
+{
+ struct kobject *p = kobj;
+ int depth = 0;
+ do {
+ depth++;
+ } while ((p = p->parent) && !is_root(p));
+ return depth;
+}
+
+static int object_path_length(struct kobject *kobj)
+{
+ struct kobject *p = kobj;
+ int length = 1;
+ do {
+ length += strlen(kobject_name(p)) + 1;
+ p = p->parent;
+ } while (p && !is_root(p));
+ return length;
+}
+
+static void fill_object_path(struct kobject *kobj, char *buffer, int length)
+{
+ struct kobject *p;
+
+ --length;
+ for (p = kobj; p && !is_root(p); p = p->parent) {
+ int cur = strlen(kobject_name(p));
+
+ /* back up enough to print this bus id with '/' */
+ length -= cur;
+ strncpy(buffer + length, kobject_name(p), cur);
+ *(buffer + --length) = '/';
+ }
+}
+
+static int sysfs_get_target_path(struct kobject *kobj, struct kobject *target, char *path)
+{
+ char *s;
+ int depth, size;
+
+ depth = object_depth(kobj);
+ size = object_path_length(target) + depth * 3 - 1;
+ if (size > PATH_MAX)
+ return -ENAMETOOLONG;
+
+ pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size);
+
+ for (s = path; depth--; s += 3)
+ strcpy(s, "../");
+
+ fill_object_path(target, path, size);
+ pr_debug("%s: path = '%s'\n", __FUNCTION__, path);
+
+ return 0;
+}
+
+static DECLARE_RWSEM(sysfs_rename_sem);
+
+static int sysfs_getlink(struct dentry *dentry, char *path)
+{
+ struct kobject *kobj, *sl_target;
+ int error = 0;
+
+ kobj = sysfs_get_kobject(dentry->d_parent);
+ if (!kobj)
+ return -EINVAL;
+
+ sl_target = sysfs_get_kobject(dentry);
+ if (!sl_target) {
+ kobject_put(kobj);
+ return -EINVAL;
+ }
+
+ down_read(&sysfs_rename_sem);
+ error = sysfs_get_target_path(kobj, sl_target, path);
+ up_read(&sysfs_rename_sem);
+
+ kobject_put(kobj);
+ kobject_put(sl_target);
+ return error;
+
+}
+
+static int sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ int error = -ENOMEM;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ if (page)
+ error = sysfs_getlink(dentry, (char *)page);
+ nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
+ return 0;
+}
+
+static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *page = nd_get_link(nd);
+ if (!IS_ERR(page))
+ free_page((unsigned long)page);
+}
+
+static struct inode_operations sysfs_symlink_inode_operations = {
+ .readlink = generic_readlink,
+ .follow_link = sysfs_follow_link,
+ .put_link = sysfs_put_link,
+};
+
+static int init_symlink(struct inode *inode)
+{
+ inode->i_op = &sysfs_symlink_inode_operations;
+ return 0;
+}
+
+/*
+ * file.c - operations for regular (text) files.
+ */
+
+struct sysfs_buffer {
+ size_t count;
+ loff_t pos;
+ char *page;
+ struct sysfs_ops *ops;
+ struct semaphore sem;
+ int needs_read_fill;
+};
+
+/*
+ * Allocate @buffer->page, if it hasn't been already, then call the
+ * kobject's show() method to fill the buffer with this attribute's
+ * data. This is called only once, on the file's first read.
+ */
+static int fill_read_buffer(struct dentry *dentry, struct sysfs_buffer *buffer)
+{
+ struct attribute *attr = to_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+ struct sysfs_ops *ops = buffer->ops;
+ int ret = 0;
+ ssize_t count;
+
+ if (!buffer->page)
+ buffer->page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!buffer->page)
+ return -ENOMEM;
+
+ count = ops->show(kobj, attr, buffer->page);
+ buffer->needs_read_fill = 0;
+ BUG_ON(count > (ssize_t) PAGE_SIZE);
+ if (count >= 0)
+ buffer->count = count;
+ else
+ ret = count;
+ return ret;
+}
+
+/*
+ * Copy the buffer we filled in fill_read_buffer() to userspace.
+ * This is done at the reader's leisure, copying and advancing
+ * the amount they specify each time.
+ * This may be called continuously until the buffer is empty.
+ */
+static int flush_read_buffer(struct sysfs_buffer *buffer, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int error;
+
+ if (*ppos > buffer->count)
+ return 0;
+
+ if (count > (buffer->count - *ppos))
+ count = buffer->count - *ppos;
+
+ error = copy_to_user(buf, buffer->page + *ppos, count);
+ if (!error)
+ *ppos += count;
+ return error ? -EFAULT : count;
+}
+
+/*
+ * Allocate @buffer->page if it hasn't been already, then
+ * copy the user-supplied buffer into it.
+ */
+static int fill_write_buffer(struct sysfs_buffer *buffer, const char __user *buf,
+ size_t count)
+{
+ int error;
+
+ if (!buffer->page)
+ buffer->page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!buffer->page)
+ return -ENOMEM;
+
+ if (count > PAGE_SIZE)
+ count = PAGE_SIZE;
+ error = copy_from_user(buffer->page, buf, count);
+ buffer->needs_read_fill = 1;
+ return error ? -EFAULT : count;
+}
+
+/*
+ * Get the correct pointers for the kobject and the attribute we're
+ * dealing with, then call the store method for the attribute,
+ * passing the buffer that we acquired in fill_write_buffer().
+ */
+static int flush_write_buffer(struct dentry *dentry, struct sysfs_buffer *buffer,
+ size_t count)
+{
+ struct attribute *attr = to_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+ struct sysfs_ops *ops = buffer->ops;
+
+ return ops->store(kobj, attr, buffer->page, count);
+}
+
+static int check_perm(struct inode *inode, struct file *file)
+{
+ struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent);
+ struct attribute *attr = to_attr(file->f_dentry);
+ struct sysfs_buffer *buffer;
+ struct sysfs_ops *ops = NULL;
+ int error = 0;
+
+ if (!kobj || !attr)
+ goto Einval;
+
+ /* Grab the module reference for this attribute if we have one */
+ if (!try_module_get(attr->owner)) {
+ error = -ENODEV;
+ goto Done;
+ }
+
+ if (!kobj->ktype)
+ goto Eaccess;
+
+ ops = kobj->ktype->sysfs_ops;
+
+ /* File needs write support.
+ * The inode's perms must say it's ok, and we must have a store method.
+ */
+ if (file->f_mode & FMODE_WRITE) {
+
+ if (!(inode->i_mode & S_IWUGO) || !ops->store)
+ goto Eaccess;
+
+ }
+
+ /* File needs read support.
+ * The inode's perms must say it's ok, and we there
+ * must be a show method for it.
+ */
+ if (file->f_mode & FMODE_READ) {
+ if (!(inode->i_mode & S_IRUGO) || !ops->show)
+ goto Eaccess;
+ }
+
+ /* No error? Great, allocate a buffer for the file, and store it
+ * it in file->private_data for easy access.
+ */
+ buffer = kmalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);
+ if (buffer) {
+ memset(buffer, 0, sizeof(struct sysfs_buffer));
+ init_MUTEX(&buffer->sem);
+ buffer->needs_read_fill = 1;
+ buffer->ops = ops;
+ file->private_data = buffer;
+ } else
+ error = -ENOMEM;
+ goto Done;
+
+ Einval:
+ error = -EINVAL;
+ goto Done;
+ Eaccess:
+ error = -EACCES;
+ module_put(attr->owner);
+ Done:
+ if (error && kobj)
+ kobject_put(kobj);
+ return error;
+}
+
+/*
+ * Userspace wants to read an attribute file. The attribute descriptor
+ * is in the file's ->d_fsdata. The target object is in the directory's
+ * ->d_fsdata.
+ *
+ * We call fill_read_buffer() to allocate and fill the buffer from the
+ * object's show() method exactly once (if the read is happening from
+ * the beginning of the file). That should fill the entire buffer with
+ * all the data the object has to offer for that attribute.
+ * We then call flush_read_buffer() to copy the buffer to userspace
+ * in the increments specified.
+ */
+static ssize_t sysfs_read_file(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct sysfs_buffer *buffer = file->private_data;
+ ssize_t retval = 0;
+
+ down(&buffer->sem);
+ if (buffer->needs_read_fill) {
+ if ((retval = fill_read_buffer(file->f_dentry, buffer)))
+ goto out;
+ }
+ pr_debug("%s: count = %d, ppos = %lld, buf = %s\n",
+ __FUNCTION__, count, *ppos, buffer->page);
+ retval = flush_read_buffer(buffer, buf, count, ppos);
+ out:
+ up(&buffer->sem);
+ return retval;
+}
+
+/*
+ * Similar to sysfs_read_file(), though working in the opposite direction.
+ * We allocate and fill the data from the user in fill_write_buffer(),
+ * then push it to the kobject in flush_write_buffer().
+ * There is no easy way for us to know if userspace is only doing a partial
+ * write, so we don't support them. We expect the entire buffer to come
+ * on the first write.
+ * Hint: if you're writing a value, first read the file, modify only the
+ * the value you're changing, then write entire buffer back.
+ */
+static ssize_t sysfs_write_file(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct sysfs_buffer *buffer = file->private_data;
+
+ down(&buffer->sem);
+ count = fill_write_buffer(buffer, buf, count);
+ if (count > 0)
+ count = flush_write_buffer(file->f_dentry, buffer, count);
+ if (count > 0)
+ *ppos += count;
+ up(&buffer->sem);
+ return count;
+}
+
+static int sysfs_open_file(struct inode *inode, struct file *filp)
+{
+ return check_perm(inode, filp);
+}
+
+static int sysfs_release(struct inode *inode, struct file *filp)
+{
+ struct kobject *kobj = to_kobj(filp->f_dentry->d_parent);
+ struct attribute *attr = to_attr(filp->f_dentry);
+ struct module *owner = attr->owner;
+ struct sysfs_buffer *buffer = filp->private_data;
+
+ if (kobj)
+ kobject_put(kobj);
+ /* After this point, attr should not be accessed. */
+ module_put(owner);
+
+ if (buffer) {
+ if (buffer->page)
+ free_page((unsigned long)buffer->page);
+ kfree(buffer);
+ }
+ return 0;
+}
+
+static struct file_operations sysfs_file_operations = {
+ .read = sysfs_read_file,
+ .write = sysfs_write_file,
+ .llseek = generic_file_llseek,
+ .open = sysfs_open_file,
+ .release = sysfs_release,
+};
+
+static int init_file(struct inode *inode)
+{
+ inode->i_size = PAGE_SIZE;
+ inode->i_fop = &sysfs_file_operations;
+ return 0;
+}
+
+static void sysfs_d_iput(struct dentry *dentry, struct inode *inode)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+
+ if (sd) {
+ BUG_ON(sd->s_dentry != dentry);
+ sd->s_dentry = NULL;
+ sysfs_put(sd);
+ }
+ iput(inode);
+}
+
+/*
+ * We _must_ delete our dentries on last dput, as the chain-to-parent
+ * behavior is required to clear the parents of default_groups.
+ */
+static int sysfs_d_delete(struct dentry *dentry)
+{
+ return 1;
+}
+
+static struct dentry_operations sysfs_dentry_ops = {
+ .d_iput = sysfs_d_iput,
+ .d_delete = sysfs_d_delete,
+};
+
+/*
+ * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent
+ */
+static struct sysfs_dirent *sysfs_new_dirent(struct sysfs_dirent *parent_sd,
+ void *element)
+{
+ struct sysfs_dirent *sd;
+
+ sd = kmem_cache_alloc(sysfs_dir_cachep, GFP_KERNEL);
+ if (!sd)
+ return NULL;
+
+ memset(sd, 0, sizeof(*sd));
+ atomic_set(&sd->s_count, 1);
+ INIT_LIST_HEAD(&sd->s_links);
+ INIT_LIST_HEAD(&sd->s_children);
+ list_add(&sd->s_sibling, &parent_sd->s_children);
+ sd->s_element = element;
+
+ return sd;
+}
+
+static int sysfs_make_dirent(struct sysfs_dirent *parent_sd, struct dentry *dentry,
+ void *element, umode_t mode, int type)
+{
+ struct sysfs_dirent *sd;
+
+ sd = sysfs_new_dirent(parent_sd, element);
+ if (!sd)
+ return -ENOMEM;
+
+ sd->s_mode = mode;
+ sd->s_type = type;
+ sd->s_dentry = dentry;
+ if (dentry) {
+ dentry->d_fsdata = sysfs_get(sd);
+ dentry->d_op = &sysfs_dentry_ops;
+ }
+
+ return 0;
+}
+
+static int sysfs_add_file(struct dentry *dir, const struct attribute *attr, int type)
+{
+ struct sysfs_dirent *parent_sd = dir->d_fsdata;
+ umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG;
+ int error = 0;
+
+ down(&dir->d_inode->i_sem);
+ error = sysfs_make_dirent(parent_sd, NULL, (void *)attr, mode, type);
+ up(&dir->d_inode->i_sem);
+
+ return error;
+}
+
+static int configfs_create_file(struct kobject *kobj, const struct attribute *attr)
+{
+ BUG_ON(!kobj || !kobj->dentry || !attr);
+
+ return sysfs_add_file(kobj->dentry, attr, CONFIGFS_ITEM_ATTR);
+}
+
+static int configfs_add_link(struct sysfs_symlink *sl, struct dentry *parent, struct dentry *dentry)
+{
+ int err = 0;
+ umode_t mode = S_IFLNK | S_IRWXUGO;
+
+ err = sysfs_create(dentry, mode, init_symlink);
+ if (!err) {
+ err = sysfs_make_dirent(parent->d_fsdata, dentry, sl,
+ mode, CONFIGFS_ITEM_LINK);
+ if (!err)
+ dentry->d_op = &sysfs_dentry_ops;
+ }
+ return err;
+}
+
+static int create_link(struct kobject *parent_kobj, struct kobject *kobj, struct dentry *dentry)
+{
+ struct sysfs_dirent *target_sd = kobj->dentry->d_fsdata;
+ struct sysfs_symlink *sl;
+ int ret;
+
+ ret = -ENOMEM;
+ sl = kmalloc(sizeof(struct sysfs_symlink), GFP_KERNEL);
+ if (sl) {
+ sl->sl_target = kobject_get(kobj);
+ /* FIXME: needs a lock, I'd bet */
+ list_add(&sl->sl_list, &target_sd->s_links);
+ ret = configfs_add_link(sl, parent_kobj->dentry, dentry);
+ if (ret) {
+ list_del_init(&sl->sl_list);
+ kobject_put(kobj);
+ kfree(sl);
+ }
+ }
+
+ return ret;
+}
+
+static int get_target(const char *symname, struct nameidata *nd, struct kobject **target)
+{
+ int ret;
+
+ ret = path_lookup(symname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, nd);
+ if (!ret) {
+ if (nd->dentry->d_sb == sysfs_sb) {
+ *target = sysfs_get_kobject(nd->dentry);
+ if (!*target) {
+ ret = -ENOENT;
+ path_release(nd);
+ }
+ } else
+ ret = -EPERM;
+ }
+
+ return ret;
+}
+
+/*
+ * dir.c - Operations for directories.
+ */
+
+static int sysfs_dir_open(struct inode *inode, struct file *file)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct sysfs_dirent *parent_sd = dentry->d_fsdata;
+
+ down(&dentry->d_inode->i_sem);
+ file->private_data = sysfs_new_dirent(parent_sd, NULL);
+ up(&dentry->d_inode->i_sem);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static int sysfs_dir_close(struct inode *inode, struct file *file)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct sysfs_dirent *cursor = file->private_data;
+
+ down(&dentry->d_inode->i_sem);
+ list_del_init(&cursor->s_sibling);
+ up(&dentry->d_inode->i_sem);
+
+ release_sysfs_dirent(cursor);
+
+ return 0;
+}
+
+/* Relationship between s_mode and the DT_xxx types */
+static inline unsigned char dt_type(struct sysfs_dirent *sd)
+{
+ return (sd->s_mode >> 12) & 15;
+}
+
+static int sysfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct sysfs_dirent *parent_sd = dentry->d_fsdata;
+ struct sysfs_dirent *cursor = filp->private_data;
+ struct list_head *p, *q = &cursor->s_sibling;
+ ino_t ino;
+ int i = filp->f_pos;
+
+ switch (i) {
+ case 0:
+ ino = dentry->d_inode->i_ino;
+ if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
+ break;
+ filp->f_pos++;
+ i++;
+ /* fallthrough */
+ case 1:
+ ino = parent_ino(dentry);
+ if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
+ break;
+ filp->f_pos++;
+ i++;
+ /* fallthrough */
+ default:
+ if (filp->f_pos == 2) {
+ list_del(q);
+ list_add(q, &parent_sd->s_children);
+ }
+ for (p = q->next; p != &parent_sd->s_children; p = p->next) {
+ struct sysfs_dirent *next;
+ const char *name;
+ int len;
+
+ next = list_entry(p, struct sysfs_dirent, s_sibling);
+ if (!next->s_element)
+ continue;
+
+ name = sysfs_get_name(next);
+ len = strlen(name);
+ if (next->s_dentry)
+ ino = next->s_dentry->d_inode->i_ino;
+ else
+ ino = iunique(sysfs_sb, 2);
+
+ if (filldir(dirent, name, len, filp->f_pos, ino,
+ dt_type(next)) < 0)
+ return 0;
+
+ list_del(q);
+ list_add(q, p);
+ p = q;
+ filp->f_pos++;
+ }
+ }
+ return 0;
+}
+
+static loff_t sysfs_dir_lseek(struct file *file, loff_t offset, int origin)
+{
+ struct dentry *dentry = file->f_dentry;
+
+ down(&dentry->d_inode->i_sem);
+ switch (origin) {
+ case 1:
+ offset += file->f_pos;
+ case 0:
+ if (offset >= 0)
+ break;
+ default:
+ up(&file->f_dentry->d_inode->i_sem);
+ return -EINVAL;
+ }
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ if (file->f_pos >= 2) {
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ struct sysfs_dirent *cursor = file->private_data;
+ struct list_head *p;
+ loff_t n = file->f_pos - 2;
+
+ list_del(&cursor->s_sibling);
+ p = sd->s_children.next;
+ while (n && p != &sd->s_children) {
+ struct sysfs_dirent *next;
+ next = list_entry(p, struct sysfs_dirent,
+ s_sibling);
+ if (next->s_element)
+ n--;
+ p = p->next;
+ }
+ list_add_tail(&cursor->s_sibling, p);
+ }
+ }
+ up(&dentry->d_inode->i_sem);
+ return offset;
+}
+
+static struct file_operations sysfs_dir_operations = {
+ .open = sysfs_dir_open,
+ .release = sysfs_dir_close,
+ .llseek = sysfs_dir_lseek,
+ .read = generic_read_dir,
+ .readdir = sysfs_readdir,
+};
+
+/* attaches attribute's sysfs_dirent to the dentry corresponding to the
+ * attribute file
+ */
+static int sysfs_attach_attr(struct sysfs_dirent *sd, struct dentry *dentry)
+{
+ struct attribute *attr = sd->s_element;
+ int error;
+
+ error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init_file);
+ if (error)
+ return error;
+
+ dentry->d_op = &sysfs_dentry_ops;
+ dentry->d_fsdata = sysfs_get(sd);
+ sd->s_dentry = dentry;
+ d_rehash(dentry);
+
+ return 0;
+}
+
+static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;
+ struct sysfs_dirent *sd;
+ int found = 0;
+ int err = 0;
+
+ list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & CONFIGFS_NOT_PINNED) {
+ const unsigned char *name = sysfs_get_name(sd);
+
+ if (strcmp(name, dentry->d_name.name))
+ continue;
+
+ found = 1;
+ err = sysfs_attach_attr(sd, dentry);
+ break;
+ }
+ }
+
+ if (!found) {
+ /*
+ * If it doesn't exist and it isn't a NOT_PINNED item,
+ * it must be negative.
+ */
+ return simple_lookup(dir, dentry, nd);
+ }
+
+ return ERR_PTR(err);
+}
+
+static struct inode_operations sysfs_dir_inode_operations;
+
+static int init_dir(struct inode *inode)
+{
+ inode->i_op = &sysfs_dir_inode_operations;
+ inode->i_fop = &sysfs_dir_operations;
+
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inode->i_nlink++;
+ return 0;
+}
+
+static int create_dir(struct kobject *k, struct dentry *p, struct dentry *d)
+{
+ int error;
+ umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
+
+ error = sysfs_create(d, mode, init_dir);
+ if (!error) {
+ error = sysfs_make_dirent(p->d_fsdata, d, k, mode, CONFIGFS_DIR);
+ if (!error) {
+ p->d_inode->i_nlink++;
+ (d)->d_op = &sysfs_dentry_ops;
+ }
+ }
+ return error;
+}
+
+static struct vfsmount *sysfs_mount = NULL;
+
+static int configfs_create_dir(struct kobject *kobj, struct dentry *dentry)
+{
+ struct dentry *parent;
+ int error = 0;
+
+ BUG_ON(!kobj);
+
+ if (kobj->parent)
+ parent = kobj->parent->dentry;
+ else if (sysfs_mount && sysfs_mount->mnt_sb)
+ parent = sysfs_mount->mnt_sb->s_root;
+ else
+ return -EFAULT;
+
+ error = create_dir(kobj, parent, dentry);
+ if (!error)
+ kobj->dentry = dentry;
+ return error;
+}
+
+static void remove_dir(struct dentry *d)
+{
+ struct dentry *parent = dget(d->d_parent);
+ struct sysfs_dirent *sd;
+
+ sd = d->d_fsdata;
+ list_del_init(&sd->s_sibling);
+ sysfs_put(sd);
+ if (d->d_inode)
+ simple_rmdir(parent->d_inode, d);
+
+ pr_debug(" o %s removing done (%d)\n", d->d_name.name, atomic_read(&d->d_count));
+
+ dput(parent);
+}
+
+/*
+ * sysfs_remove_dir - remove a kobject's directory.
+ * @kobj: kobject we're removing.
+ *
+ * The only thing special about this is that we remove any files in
+ * the directory before we remove the directory
+ */
+static void configfs_remove_dir(struct kobject *kobj)
+{
+ struct dentry *dentry = dget(kobj->dentry);
+
+ if (!dentry)
+ return;
+
+ remove_dir(dentry);
+ /* Drop reference from dget() on entrance. */
+ dput(dentry);
+}
+
+#if 0
+int sysfs_rename_dir(struct kobject *kobj, const char *new_name)
+{
+ int error = 0;
+ struct dentry *new_dentry, *parent;
+
+ if (!strcmp(kobject_name(kobj), new_name))
+ return -EINVAL;
+
+ if (!kobj->parent)
+ return -EINVAL;
+
+ down_write(&sysfs_rename_sem);
+ parent = kobj->parent->dentry;
+
+ down(&parent->d_inode->i_sem);
+
+ new_dentry = lookup_one_len(new_name, parent, strlen(new_name));
+ if (!IS_ERR(new_dentry)) {
+ if (!new_dentry->d_inode) {
+ error = kobject_set_name(kobj, "%s", new_name);
+ if (!error) {
+ d_add(new_dentry, NULL);
+ d_move(kobj->dentry, new_dentry);
+ } else
+ d_delete(new_dentry);
+ } else
+ error = -EEXIST;
+ dput(new_dentry);
+ }
+ up(&parent->d_inode->i_sem);
+ up_write(&sysfs_rename_sem);
+
+ return error;
+}
+#endif
+
+/*
+ * Only subdirectories count here. Files (CONFIGFS_NOT_PINNED) are
+ * attributes and are removed by rmdir(). We recurse, taking i_sem
+ * on all children that are candidates for default detach. If the
+ * result is clean, then sysfs_detach_group() will handle dropping
+ * i_sem. If there is an error, the caller will clean up the i_sem
+ * holders via sysfs_detach_rollback().
+ */
+static int sysfs_detach_prep(struct dentry *dentry)
+{
+ struct sysfs_dirent *parent_sd = dentry->d_fsdata;
+ struct sysfs_dirent *sd;
+ int ret = 0;
+
+ if (!list_empty(&parent_sd->s_links))
+ return -EBUSY;
+
+ list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & CONFIGFS_NOT_PINNED)
+ continue;
+ if (sd->s_type & CONFIGFS_USET_DEFAULT) {
+ down(&sd->s_dentry->d_inode->i_sem);
+ /* Mark that we've taken i_sem */
+ sd->s_type |= CONFIGFS_USET_DROPPING;
+
+ ret = sysfs_detach_prep(sd->s_dentry);
+ if (!ret)
+ continue;
+ } else
+ ret = -ENOTEMPTY;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Walk the tree, dropping i_sem wherever CONFIGFS_USET_DROPPING is
+ * set.
+ */
+static void sysfs_detach_rollback(struct dentry *dentry)
+{
+ struct sysfs_dirent *parent_sd = dentry->d_fsdata;
+ struct sysfs_dirent *sd;
+
+ list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & CONFIGFS_USET_DEFAULT) {
+ sysfs_detach_rollback(sd->s_dentry);
+
+ if (sd->s_type & CONFIGFS_USET_DROPPING) {
+ sd->s_type &= ~CONFIGFS_USET_DROPPING;
+ up(&sd->s_dentry->d_inode->i_sem);
+ }
+ }
+ }
+}
+
+static void detach_attrs(struct kobject *kobj)
+{
+ struct dentry *dentry = dget(kobj->dentry);
+ struct sysfs_dirent *parent_sd;
+ struct sysfs_dirent *sd, *tmp;
+
+ if (!dentry)
+ return;
+
+ pr_debug("configfs %s: dropping attrs for dir\n", dentry->d_name.name);
+
+ parent_sd = dentry->d_fsdata;
+ list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
+ if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED))
+ continue;
+ list_del_init(&sd->s_sibling);
+ sysfs_drop_dentry(sd, dentry);
+ sysfs_put(sd);
+ }
+
+ /* Drop reference from dget() on entrance. */
+ dput(dentry);
+}
+
+/*
+ * All of link_obj/unlink_obj/link_group/unlink_group require that
+ * subsys->su_sem is held.
+ */
+
+static void sysfs_detach_group(struct kobject *kobj);
+
+static void detach_groups(struct kset *group)
+{
+ struct dentry *dentry = dget(group->kobj.dentry);
+ struct dentry *child;
+ struct sysfs_dirent *parent_sd;
+ struct sysfs_dirent *sd, *tmp;
+
+ if (!dentry)
+ return;
+
+ parent_sd = dentry->d_fsdata;
+ list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
+ if (!sd->s_element || !(sd->s_type & CONFIGFS_USET_DEFAULT))
+ continue;
+
+ child = sd->s_dentry;
+
+ sysfs_detach_group(sd->s_element);
+ child->d_inode->i_flags |= S_DEAD;
+
+ /*
+ * From rmdir/unregister, a sysfs_detach_prep() pass
+ * has taken our i_sem for us. Drop it.
+ * From mkdir/register cleanup, there is no sem held.
+ */
+ if (sd->s_type & CONFIGFS_USET_DROPPING)
+ up(&child->d_inode->i_sem);
+
+ d_delete(child);
+ dput(child);
+ }
+
+ /**
+ * Drop reference from dget() on entrance.
+ */
+ dput(dentry);
+}
+
+static void sysfs_detach_item(struct kobject *kobj)
+{
+ detach_attrs(kobj);
+ configfs_remove_dir(kobj);
+}
+
+static void unlink_obj(struct kobject *kobj)
+{
+ struct kset *group;
+
+ group = kobj->kset;
+ if (group) {
+ list_del_init(&kobj->entry);
+
+ kobj->kset = NULL;
+ kobj->parent = NULL;
+ kobject_put(kobj);
+
+ config_group_put(group);
+ }
+}
+
+static void unlink_group(struct kset *group)
+{
+ int i;
+ struct kset *new_group;
+
+ if (group->default_groups) {
+ for (i = 0; group->default_groups[i]; i++) {
+ new_group = group->default_groups[i];
+ unlink_group(new_group);
+ }
+ }
+
+ group->subsys = NULL;
+ unlink_obj(&group->kobj);
+}
+
+static void sysfs_detach_group(struct kobject *kobj)
+{
+ detach_groups(to_config_group(kobj));
+ sysfs_detach_item(kobj);
+}
+
+/*
+ * Drop the initial reference from make_item()/make_group()
+ * This function assumes that reference is held on item
+ * and that item holds a valid reference to the parent. Also, it
+ * assumes the caller has validated type.
+ */
+static void client_drop_item(struct kobject *parent_kobj, struct kobject *kobj)
+{
+ struct kobj_type *type;
+
+ type = parent_kobj->ktype;
+ BUG_ON(!type);
+
+ if (type->ct_group_ops && type->ct_group_ops->drop_item)
+ type->ct_group_ops->drop_item(to_config_group(parent_kobj), kobj);
+ else
+ kobject_put(kobj);
+}
+
+static int sysfs_attach_group(struct kobject *parent_kobj, struct kobject *kobj, struct dentry *dentry);
+
+static int populate_attrs(struct kobject *kobj)
+{
+ struct kobj_type *t = kobj->ktype;
+ struct attribute *attr;
+ int error = 0;
+ int i;
+
+ if (!t)
+ return -EINVAL;
+ if (t->default_attrs) {
+ for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
+ if ((error = configfs_create_file(kobj, attr)))
+ break;
+ }
+ }
+
+ if (error)
+ detach_attrs(kobj);
+
+ return error;
+}
+
+/*
+ * The goal is that sysfs_attach_item() (and
+ * sysfs_attach_group()) can be called from either the VFS or this
+ * module. That is, they assume that the items have been created,
+ * the dentry allocated, and the dcache is all ready to go.
+ *
+ * If they fail, they must clean up after themselves as if they
+ * had never been called. The caller (VFS or local function) will
+ * handle cleaning up the dcache bits.
+ *
+ * sysfs_detach_group() and sysfs_detach_item() behave similarly on
+ * the way out. They assume that the proper semaphores are held, they
+ * clean up the configfs items, and they expect their callers will
+ * handle the dcache bits.
+ */
+static int sysfs_attach_item(struct kobject *parent_kobj, struct kobject *kobj, struct dentry *dentry)
+{
+ int ret;
+
+ ret = configfs_create_dir(kobj, dentry);
+ if (!ret) {
+ ret = populate_attrs(kobj);
+ if (ret) {
+ configfs_remove_dir(kobj);
+ d_delete(dentry);
+ }
+ }
+ return ret;
+}
+
+/*
+ * This fakes mkdir(2) on a default_groups[] entry. It
+ * creates a dentry, attachs it, and then does fixup
+ * on the sd->s_type.
+ *
+ * We could, perhaps, tweak our parent's ->mkdir for a minute and
+ * try using vfs_mkdir. Just a thought.
+ */
+static int create_default_group(struct kset *parent_group, struct kset *group)
+{
+ int ret;
+ struct qstr name;
+ struct sysfs_dirent *sd;
+ /* We trust the caller holds a reference to parent */
+ struct dentry *child, *parent = parent_group->kobj.dentry;
+
+ if (!group->kobj.k_name)
+ group->kobj.k_name = group->kobj.name;
+ name.name = group->kobj.k_name;
+ name.len = strlen(name.name);
+ name.hash = full_name_hash(name.name, name.len);
+
+ ret = -ENOMEM;
+ child = d_alloc(parent, &name);
+ if (child) {
+ d_add(child, NULL);
+
+ ret = sysfs_attach_group(&parent_group->kobj,
+ &group->kobj, child);
+ if (!ret) {
+ sd = child->d_fsdata;
+ sd->s_type |= CONFIGFS_USET_DEFAULT;
+ } else {
+ d_delete(child);
+ dput(child);
+ }
+ }
+ return ret;
+}
+
+static int populate_groups(struct kset *group)
+{
+ struct kset *new_group;
+ struct dentry *dentry = group->kobj.dentry;
+ int ret = 0;
+ int i;
+
+ if (group && group->default_groups) {
+ /* FYI, we're faking mkdir here
+ * I'm not sure we need this semaphore, as we're called
+ * from our parent's mkdir. That holds our parent's
+ * i_sem, so afaik lookup cannot continue through our
+ * parent to find us, let alone mess with our tree.
+ * That said, taking our i_sem is closer to mkdir
+ * emulation, and shouldn't hurt. */
+ down(&dentry->d_inode->i_sem);
+
+ for (i = 0; group->default_groups[i]; i++) {
+ new_group = group->default_groups[i];
+
+ ret = create_default_group(group, new_group);
+ if (ret)
+ break;
+ }
+
+ up(&dentry->d_inode->i_sem);
+ }
+
+ if (ret)
+ detach_groups(group);
+ return ret;
+}
+
+static int sysfs_attach_group(struct kobject *parent_kobj, struct kobject *kobj, struct dentry *dentry)
+{
+ int ret;
+ struct sysfs_dirent *sd;
+
+ ret = sysfs_attach_item(parent_kobj, kobj, dentry);
+ if (!ret) {
+ sd = dentry->d_fsdata;
+ sd->s_type |= CONFIGFS_USET_DIR;
+
+ ret = populate_groups(to_config_group(kobj));
+ if (ret) {
+ sysfs_detach_item(kobj);
+ d_delete(dentry);
+ }
+ }
+ return ret;
+}
+
+static void link_obj(struct kobject *parent_kobj, struct kobject *kobj)
+{
+ /* Parent seems redundant with group, but it makes certain
+ * traversals much nicer. */
+ kobj->parent = parent_kobj;
+ kobj->kset = config_group_get(to_config_group(parent_kobj));
+ list_add_tail(&kobj->entry, &kobj->kset->cg_children);
+
+ kobject_get(kobj);
+}
+
+static void link_group(struct kset *parent_group, struct kset *group)
+{
+ int i;
+ struct kset *new_group;
+ struct subsystem *subsys = NULL; /* gcc is a turd */
+
+ link_obj(&parent_group->kobj, &group->kobj);
+
+ if (parent_group->subsys)
+ subsys = parent_group->subsys;
+ else if (is_root(&parent_group->kobj))
+ subsys = to_configfs_subsystem(group);
+ else
+ BUG();
+ group->subsys = subsys;
+
+ if (group->default_groups) {
+ for (i = 0; group->default_groups[i]; i++) {
+ new_group = group->default_groups[i];
+ link_group(group, new_group);
+ }
+ }
+}
+
+static int sysfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int ret;
+ struct kset *group;
+ struct kobject *kobj;
+ struct kobject *parent_kobj;
+ struct subsystem *subsys;
+ struct sysfs_dirent *sd;
+ struct kobj_type *type;
+ struct module *owner;
+ char *name;
+
+ if (dentry->d_parent == sysfs_sb->s_root)
+ return -EPERM;
+
+ sd = dentry->d_parent->d_fsdata;
+ if (!(sd->s_type & CONFIGFS_USET_DIR))
+ return -EPERM;
+
+ parent_kobj = sysfs_get_kobject(dentry->d_parent);
+ type = parent_kobj->ktype;
+ subsys = to_config_group(parent_kobj)->subsys;
+ BUG_ON(!subsys);
+
+ if (!type || !type->ct_group_ops ||
+ (!type->ct_group_ops->make_group &&
+ !type->ct_group_ops->make_item)) {
+ kobject_put(parent_kobj);
+ return -EPERM; /* What lack-of-mkdir returns */
+ }
+
+ name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL);
+ if (!name) {
+ kobject_put(parent_kobj);
+ return -ENOMEM;
+ }
+ snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name);
+
+ down_write(&subsys->rwsem);
+ group = NULL;
+ kobj = NULL;
+ if (type->ct_group_ops->make_group) {
+ group = type->ct_group_ops->make_group(to_config_group(parent_kobj), name);
+ if (group) {
+ link_group(to_config_group(parent_kobj), group);
+ kobj = &group->kobj;
+ }
+ } else {
+ kobj = type->ct_group_ops->make_item(to_config_group(parent_kobj), name);
+ if (kobj)
+ link_obj(parent_kobj, kobj);
+ }
+ up_write(&subsys->rwsem);
+
+ kfree(name);
+ if (!kobj) {
+ kobject_put(parent_kobj);
+ return -ENOMEM;
+ }
+
+ ret = -EINVAL;
+ type = kobj->ktype;
+ if (type) {
+ owner = type->ct_owner;
+ if (try_module_get(owner)) {
+ ret = group ?
+ sysfs_attach_group(parent_kobj, kobj, dentry) :
+ sysfs_attach_item(parent_kobj, kobj, dentry);
+
+ if (ret) {
+ down_write(&subsys->rwsem);
+ if (group)
+ unlink_group(group);
+ else
+ unlink_obj(kobj);
+ client_drop_item(parent_kobj, kobj);
+ up_write(&subsys->rwsem);
+
+ kobject_put(parent_kobj);
+ module_put(owner);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int sysfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct kobject *parent_kobj;
+ struct kobject *kobj;
+ struct subsystem *subsys;
+ struct sysfs_dirent *sd;
+ struct module *owner = NULL;
+ int ret;
+
+ if (dentry->d_parent == sysfs_sb->s_root)
+ return -EPERM;
+
+ sd = dentry->d_fsdata;
+ if (sd->s_type & CONFIGFS_USET_DEFAULT)
+ return -EPERM;
+
+ parent_kobj = sysfs_get_kobject(dentry->d_parent);
+ subsys = to_config_group(parent_kobj)->subsys;
+ BUG_ON(!subsys);
+
+ if (!parent_kobj->ktype) {
+ kobject_put(parent_kobj);
+ return -EINVAL;
+ }
+
+ ret = sysfs_detach_prep(dentry);
+ if (ret) {
+ sysfs_detach_rollback(dentry);
+ kobject_put(parent_kobj);
+ return ret;
+ }
+
+ kobj = sysfs_get_kobject(dentry);
+
+ /* Drop reference from above, item already holds one. */
+ kobject_put(parent_kobj);
+
+ if (kobj->ktype)
+ owner = kobj->ktype->ct_owner;
+
+ if (sd->s_type & CONFIGFS_USET_DIR) {
+ sysfs_detach_group(kobj);
+
+ down_write(&subsys->rwsem);
+ unlink_group(to_config_group(kobj));
+ } else {
+ sysfs_detach_item(kobj);
+
+ down_write(&subsys->rwsem);
+ unlink_obj(kobj);
+ }
+
+ client_drop_item(parent_kobj, kobj);
+ up_write(&subsys->rwsem);
+
+ /* Drop our reference from above */
+ kobject_put(kobj);
+
+ module_put(owner);
+
+ return 0;
+}
+
+int sysfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+ int ret;
+ struct nameidata nd;
+ struct kobject *parent_kobj;
+ struct kobject *sl_target;
+ struct kobj_type *type;
+
+ ret = -EPERM; /* What lack-of-symlink returns */
+ if (dentry->d_parent == sysfs_sb->s_root)
+ goto out;
+
+ parent_kobj = sysfs_get_kobject(dentry->d_parent);
+ type = parent_kobj->ktype;
+
+ if (!type || !type->sysfs_ops || !type->sysfs_ops->allow_link)
+ goto out_put;
+
+ ret = get_target(symname, &nd, &sl_target);
+ if (ret)
+ goto out_put;
+
+ ret = type->sysfs_ops->allow_link(parent_kobj, sl_target);
+ if (!ret)
+ ret = create_link(parent_kobj, sl_target, dentry);
+
+ kobject_put(sl_target);
+ path_release(&nd);
+
+ out_put:
+ kobject_put(parent_kobj);
+
+ out:
+ return ret;
+}
+
+static int sysfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ struct sysfs_symlink *sl;
+ struct kobject *parent_kobj;
+ struct kobj_type *type;
+ int ret;
+
+ ret = -EPERM; /* What lack-of-symlink returns */
+ if (!(sd->s_type & CONFIGFS_ITEM_LINK))
+ goto out;
+
+ if (dentry->d_parent == sysfs_sb->s_root)
+ BUG();
+
+ sl = sd->s_element;
+
+ parent_kobj = sysfs_get_kobject(dentry->d_parent);
+ type = parent_kobj->ktype;
+
+ list_del_init(&sd->s_sibling);
+ sysfs_drop_dentry(sd, dentry->d_parent);
+ dput(dentry);
+ sysfs_put(sd);
+
+ /*
+ * drop_link() must be called before
+ * list_del_init(&sl->sl_list), so that the order of
+ * drop_link(this, target) and drop_item(target) is preserved.
+ */
+ if (type && type->sysfs_ops && type->sysfs_ops->drop_link)
+ type->sysfs_ops->drop_link(parent_kobj, sl->sl_target);
+
+ /* FIXME: Needs lock */
+ list_del_init(&sl->sl_list);
+
+ /* Put reference from create_link() */
+ kobject_put(sl->sl_target);
+ kfree(sl);
+
+ kobject_put(parent_kobj);
+
+ ret = 0;
+
+ out:
+ return ret;
+}
+
+static struct inode_operations sysfs_dir_inode_operations = {
+ .lookup = sysfs_lookup,
+ .mkdir = sysfs_mkdir,
+ .rmdir = sysfs_rmdir,
+ .symlink = sysfs_symlink,
+ .unlink = sysfs_unlink,
+};
+
+static int sysfs_mnt_count = 0;
+
+int sysfs_pin_fs(void)
+{
+ return simple_pin_fs("configfs", &sysfs_mount, &sysfs_mnt_count);
+}
+
+void sysfs_release_fs(void)
+{
+ simple_release_fs(&sysfs_mount, &sysfs_mnt_count);
+}
+
+int configfs_register_subsystem(struct subsystem *subsys)
+{
+ int err;
+ struct kset *group = &subsys->kset;
+ struct qstr name;
+ struct dentry *dentry;
+ struct sysfs_dirent *sd;
+
+ err = sysfs_pin_fs();
+ if (err)
+ return err;
+
+ if (!group->kobj.k_name)
+ group->kobj.k_name = group->kobj.name;
+
+ sd = sysfs_sb->s_root->d_fsdata;
+ link_group(to_config_group(sd->s_element), group);
+
+ down(&sysfs_sb->s_root->d_inode->i_sem);
+
+ name.name = group->kobj.k_name;
+ name.len = strlen(name.name);
+ name.hash = full_name_hash(name.name, name.len);
+
+ err = -ENOMEM;
+ dentry = d_alloc(sysfs_sb->s_root, &name);
+ if (!dentry)
+ goto out_release;
+
+ d_add(dentry, NULL);
+
+ err = sysfs_attach_group(sd->s_element, &group->kobj, dentry);
+ if (!err)
+ dentry = NULL;
+ else
+ d_delete(dentry);
+
+ up(&sysfs_sb->s_root->d_inode->i_sem);
+
+ if (dentry) {
+ dput(dentry);
+ out_release:
+ unlink_group(group);
+ sysfs_release_fs();
+ }
+
+ return err;
+}
+
+void configfs_unregister_subsystem(struct subsystem *subsys)
+{
+ struct kset *group = &subsys->kset;
+ struct dentry *dentry = group->kobj.dentry;
+
+ if (dentry->d_parent != sysfs_sb->s_root) {
+ printk(KERN_ERR
+ "configfs: Tried to unregister non-subsystem!\n");
+ return;
+ }
+
+ down(&sysfs_sb->s_root->d_inode->i_sem);
+ down(&dentry->d_inode->i_sem);
+ if (sysfs_detach_prep(dentry)) {
+ printk(KERN_ERR
+ "configfs: Tried to unregister non-empty subsystem!\n");
+ }
+ sysfs_detach_group(&group->kobj);
+ dentry->d_inode->i_flags |= S_DEAD;
+ up(&dentry->d_inode->i_sem);
+
+ d_delete(dentry);
+
+ up(&sysfs_sb->s_root->d_inode->i_sem);
+
+ dput(dentry);
+
+ unlink_group(group);
+ sysfs_release_fs();
+}
+
+/*
+ * mount.c - operations for initializing and mounting
+ */
+
+#define CONFIGFS_MAGIC 0x62656570
+
+static struct super_operations sysfs_ops = {
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
+};
+
+static struct sysfs_dirent sysfs_root = {
+ .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling),
+ .s_children = LIST_HEAD_INIT(sysfs_root.s_children),
+ .s_element = &sysfs_root_group.kobj,
+ .s_type = CONFIGFS_ROOT,
+};
+
+static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct inode *inode;
+ struct dentry *root;
+
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = CONFIGFS_MAGIC;
+ sb->s_op = &sysfs_ops;
+ sysfs_sb = sb;
+
+ inode = sysfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO);
+ if (inode) {
+ inode->i_op = &sysfs_dir_inode_operations;
+ inode->i_fop = &sysfs_dir_operations;
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inode->i_nlink++;
+ } else {
+ pr_debug("configfs: could not get root inode\n");
+ return -ENOMEM;
+ }
+
+ root = d_alloc_root(inode);
+ if (!root) {
+ pr_debug("%s: could not get root dentry!\n", __FUNCTION__);
+ iput(inode);
+ return -ENOMEM;
+ }
+ config_group_init(&sysfs_root_group);
+ sysfs_root_group.kobj.dentry = root;
+ root->d_fsdata = &sysfs_root;
+ sb->s_root = root;
+ return 0;
+}
+
+static struct super_block *sysfs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return get_sb_single(fs_type, flags, data, sysfs_fill_super);
+}
+
+static struct file_system_type sysfs_fs_type = {
+ .name = "configfs",
+ .owner = THIS_MODULE,
+ .get_sb = sysfs_get_sb,
+ .kill_sb = kill_litter_super,
+};
+
+static int __init sysfs_init(void)
+{
+ int err = -ENOMEM;
+
+ sysfs_dir_cachep = kmem_cache_create("configfs_dir_cache",
+ sizeof(struct sysfs_dirent), 0, 0, NULL, NULL);
+ if (!sysfs_dir_cachep)
+ goto out;
+
+ err = register_filesystem(&sysfs_fs_type);
+ if (err)
+ goto out_err;
+ out:
+ return err;
+ out_err:
+ kmem_cache_destroy(sysfs_dir_cachep);
+ sysfs_dir_cachep = NULL;
+ goto out;
+}
+
+static void __exit sysfs_exit(void)
+{
+ unregister_filesystem(&sysfs_fs_type);
+}
+
+MODULE_AUTHOR("Oracle");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.1");
+MODULE_DESCRIPTION
+ ("Simple RAM filesystem for user driven kernel subsystem configuration.");
+
+module_init(sysfs_init);
+module_exit(sysfs_exit);
+
+/*
+ * configfs client helpers
+ */
+
+void config_group_init_type_name(struct kset *group, const char *name, struct kobj_type *type)
+{
+ kobject_set_name(&group->kobj, name);
+ group->kobj.ktype = type;
+ config_group_init(group);
+}
+
+void config_group_init(struct kset *group)
+{
+ kobject_init(&group->kobj);
+ INIT_LIST_HEAD(&group->cg_children);
+}
+
+void kobject_init_type_name(struct kobject *kobj, const char *name, struct kobj_type *type)
+{
+ kobject_set_name(kobj, name);
+ kobj->ktype = type;
+ kobject_init(kobj);
+}
+
+EXPORT_SYMBOL(configfs_register_subsystem);
+EXPORT_SYMBOL(configfs_unregister_subsystem);
+EXPORT_SYMBOL(config_group_init_type_name);
+EXPORT_SYMBOL(config_group_init);
+EXPORT_SYMBOL(kobject_init_type_name);
^ permalink raw reply [flat|nested] 22+ messages in thread
* [RFC][PATCH 4 of 4] Configfs is really sysfs
2005-08-30 22:59 ` [RFC][PATCH 3 " Daniel Phillips
@ 2005-08-30 23:03 ` Daniel Phillips
2005-08-30 23:30 ` Daniel Phillips
2005-08-30 23:06 ` [RFC][PATCH 3 " Stephen Hemminger
2005-08-30 23:10 ` Daniel Phillips
2 siblings, 1 reply; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 23:03 UTC (permalink / raw)
To: linux-kernel; +Cc: Andrew Morton, Joel Becker, Greg KH
A kernel code example that uses the modified configfs to define a simple
configuration interface. Note the use of kobjects and ksets instead of
config_items and config_groups.
Also notice how much code is required to get a simple value from
userspace to kernel space. This is a big problem that needs to be
addressed soon, before we end up with tens or hundreds of thousands of
lines of code code bloat just to get and set variables from user space.
Regards,
Daniel
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/configfs.h>
struct ddbond_info {
struct kobject item;
int controlsock;
};
static inline struct ddbond_info *to_ddbond_info(struct kobject *item)
{
return container_of(item, struct ddbond_info, item);
}
static ssize_t ddbond_info_attr_show(struct kobject *item,
struct attribute *attr, char *page)
{
ssize_t count;
struct ddbond_info *ddbond_info = to_ddbond_info(item);
count = sprintf(page, "%d\n", ddbond_info->controlsock);
return count;
}
static ssize_t ddbond_info_attr_store(struct kobject *item,
struct attribute *attr, const char *page, size_t count)
{
struct ddbond_info *ddbond_info = to_ddbond_info(item);
unsigned long tmp;
char *p = (char *)page;
tmp = simple_strtoul(p, &p, 10);
if (!p || (*p && (*p != '\n')))
return -EINVAL;
if (tmp > INT_MAX)
return -ERANGE;
ddbond_info->controlsock = tmp;
return count;
}
static void ddbond_info_release(struct kobject *item)
{
kfree(to_ddbond_info(item));
}
static struct kobj_type ddbond_info_type = {
.sysfs_ops = &(struct sysfs_ops){
.show = ddbond_info_attr_show,
.store = ddbond_info_attr_store,
.release = ddbond_info_release,
},
.default_attrs = (struct attribute *[]){
&(struct attribute){
.owner = THIS_MODULE,
.name = "sockname",
.mode = S_IRUGO | S_IWUSR,
},
NULL,
},
.ct_owner = THIS_MODULE,
};
static struct kobject *ddbond_make_item(struct kset *group, const char *name)
{
struct ddbond_info *ddbond_info;
if (!(ddbond_info = kcalloc(1, sizeof(struct ddbond_info), GFP_KERNEL)))
return NULL;
kobject_init_type_name(&ddbond_info->item, name, &ddbond_info_type);
return &ddbond_info->item;
}
static ssize_t ddbond_description(struct kobject *item,
struct attribute *attr, char *page)
{
return sprintf(page,
"A ddbond block server has two components: a userspace server and a kernel\n"
"io daemon. First start the server and give it the name of a socket it will\n"
"create, then create a ddbond directory and write the same name into the\n"
"socket attribute\n");
}
static struct kobj_type ddbond_type = {
.sysfs_ops = &(struct sysfs_ops){
.show = ddbond_description,
},
.ct_group_ops = &(struct configfs_group_operations){
.make_item = ddbond_make_item,
},
.default_attrs = (struct attribute *[]){
&(struct attribute){
.owner = THIS_MODULE,
.name = "description",
.mode = S_IRUGO,
},
NULL,
}
};
static struct subsystem ddbond_subsys = {
.kset = {
.kobj = {
.k_name = "ddbond",
.ktype = &ddbond_type,
},
},
};
static int __init init_ddbond_config(void)
{
int ret;
config_group_init(&ddbond_subsys.kset);
init_rwsem(&ddbond_subsys.rwsem);
if ((ret = configfs_register_subsystem(&ddbond_subsys)))
printk(KERN_ERR "Error %d while registering subsystem %s\n",
ret, ddbond_subsys.kset.kobj.k_name);
return ret;
}
static void __exit exit_ddbond_config(void)
{
configfs_unregister_subsystem(&ddbond_subsys);
}
module_init(init_ddbond_config);
module_exit(exit_ddbond_config);
MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 3 of 4] Configfs is really sysfs
2005-08-30 22:59 ` [RFC][PATCH 3 " Daniel Phillips
2005-08-30 23:03 ` [RFC][PATCH 4 " Daniel Phillips
@ 2005-08-30 23:06 ` Stephen Hemminger
2005-08-30 23:18 ` Daniel Phillips
2005-08-30 23:10 ` Daniel Phillips
2 siblings, 1 reply; 22+ messages in thread
From: Stephen Hemminger @ 2005-08-30 23:06 UTC (permalink / raw)
To: linux-kernel
On Wed, 31 Aug 2005 08:59:55 +1000
Daniel Phillips <phillips@istop.com> wrote:
> Configfs rewritten as a single file and updated to use kobjects instead of its
> own clone of kobjects (config_items).
>
Some style issues:
Mixed case in labels
Bad identation
> +static int sysfs_create(struct dentry *dentry, int mode, int (*init) (struct inode *))
> +{
> + int error = 0;
> + struct inode *inode = NULL;
> + if (dentry) {
> + if (!dentry->d_inode) {
> + if ((inode = sysfs_new_inode(mode))) {
> + if (dentry->d_parent
> + && dentry->d_parent->d_inode) {
> + struct inode *p_inode =
> + dentry->d_parent->d_inode;
> + p_inode->i_mtime = p_inode->i_ctime =
> + CURRENT_TIME;
> + }
> + goto Proceed;
> + } else
> + error = -ENOMEM;
> + } else
> + error = -EEXIST;
> + } else
> + error = -ENOENT;
> + goto Done;
> +
> + Proceed:
> + if (init)
> + error = init(inode);
> + if (!error) {
> + d_instantiate(dentry, inode);
> + if (S_ISDIR(mode) || S_ISLNK(mode)) /* pin link and directory dentries */
> + dget(dentry);
> + } else
> + iput(inode);
> + Done:
Why the mixed case label?
> + return error;
> +}
> +/*
> + * configfs client helpers
> + */
> +
> +void config_group_init_type_name(struct kset *group, const char *name, struct kobj_type *type)
> +{
> + kobject_set_name(&group->kobj, name);
> + group->kobj.ktype = type;
> + config_group_init(group);
> +}
Use tabs not one space for indent.
> +void config_group_init(struct kset *group)
> +{
> + kobject_init(&group->kobj);
> + INIT_LIST_HEAD(&group->cg_children);
> +}
> +
> +void kobject_init_type_name(struct kobject *kobj, const char *name, struct kobj_type *type)
> +{
> + kobject_set_name(kobj, name);
> + kobj->ktype = type;
> + kobject_init(kobj);
> +}
> +
> +EXPORT_SYMBOL(configfs_register_subsystem);
> +EXPORT_SYMBOL(configfs_unregister_subsystem);
> +EXPORT_SYMBOL(config_group_init_type_name);
> +EXPORT_SYMBOL(config_group_init);
> +EXPORT_SYMBOL(kobject_init_type_name);
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 3 of 4] Configfs is really sysfs
2005-08-30 22:59 ` [RFC][PATCH 3 " Daniel Phillips
2005-08-30 23:03 ` [RFC][PATCH 4 " Daniel Phillips
2005-08-30 23:06 ` [RFC][PATCH 3 " Stephen Hemminger
@ 2005-08-30 23:10 ` Daniel Phillips
2 siblings, 0 replies; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 23:10 UTC (permalink / raw)
To: linux-kernel; +Cc: Andrew Morton
On Wednesday 31 August 2005 08:59, Daniel Phillips wrote:
> -obj-$(CONFIG_CONFIGFS_FS) += configfs.o
> +obj-$(CONFIG_CONFIGFS_FS) += configfs.o ddbond.config.o
This should just be:
+obj-$(CONFIG_CONFIGFS_FS) += configfs.o
However, the wrong version does provide a convenient way of compiling the
example, I just... have... to... remember to delete it next time.
Regards,
Daniel
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-08-30 22:54 [RFC][PATCH 1 of 4] Configfs is really sysfs Daniel Phillips
2005-08-30 22:57 ` [RFC][PATCH 2 " Daniel Phillips
@ 2005-08-30 23:13 ` Joel Becker
2005-08-30 23:25 ` Daniel Phillips
2005-08-30 23:28 ` Andrew Morton
1 sibling, 2 replies; 22+ messages in thread
From: Joel Becker @ 2005-08-30 23:13 UTC (permalink / raw)
To: Daniel Phillips; +Cc: linux-kernel, Andrew Morton, Greg KH
[-- Attachment #1: Type: text/plain, Size: 888 bytes --]
On Wed, Aug 31, 2005 at 08:54:39AM +1000, Daniel Phillips wrote:
> But it would be stupid to forbid users from creating directories in sysfs or
> to forbid kernel modules from directly tweaking a configfs namespace. Why
> should the kernel not be able to add objects to a directory a user created?
> It should be up to the module author to decide these things.
This is precisely why configfs is separate from sysfs. If both
user and kernel can create objects, the lifetime of the object and its
filesystem representation is very complex. Sysfs already has problems
with people getting this wrong. configfs does not.
The fact that sysfs and configfs have similar backing stores
does not make them the same thing.
Joel
--
"Against stupidity the Gods themselves contend in vain."
- Freidrich von Schiller
http://www.jlbec.org/
jlbec@evilplan.org
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 3 of 4] Configfs is really sysfs
2005-08-30 23:06 ` [RFC][PATCH 3 " Stephen Hemminger
@ 2005-08-30 23:18 ` Daniel Phillips
0 siblings, 0 replies; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 23:18 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: linux-kernel
On Tuesday 30 August 2005 19:06, Stephen Hemminger wrote:
> On Wed, 31 Aug 2005 08:59:55 +1000
>
> Daniel Phillips <phillips@istop.com> wrote:
> > Configfs rewritten as a single file and updated to use kobjects instead
> > of its own clone of kobjects (config_items).
>
> Some style issues:
> Mixed case in labels
I certainly agree. This is strictly for comparison purposes and so I did not
clean up the stylistic problems from the original... this time.
> Bad identation
I did lindent it however :-)
> > + Done:
>
> Why the mixed case label?
It shall die.
> > +void config_group_init_type_name(struct kset *group, const char *name,
> > struct kobj_type *type) +{
> > + kobject_set_name(&group->kobj, name);
> > + group->kobj.ktype = type;
> > + config_group_init(group);
> > +}
>
> Use tabs not one space for indent.
Urk. Kmail did that to me, it has been broken that way for a year or so. I
will have to repost the whole set from a mailer that works.
Regards,
Daniel
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 2 of 4] Configfs is really sysfs
2005-08-30 22:57 ` [RFC][PATCH 2 " Daniel Phillips
2005-08-30 22:59 ` [RFC][PATCH 3 " Daniel Phillips
@ 2005-08-30 23:22 ` Daniel Phillips
1 sibling, 0 replies; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 23:22 UTC (permalink / raw)
To: linux-kernel; +Cc: Andrew Morton, Joel Becker, Greg KH
(avoiding the kmail formatting problems this time.)
Sysfs rearranged as a single file for analysis purposes.
diff -up --recursive 2.6.13-rc5-mm1.clean/fs/sysfs/Makefile 2.6.13-rc5-mm1/fs/sysfs/Makefile
--- 2.6.13-rc5-mm1.clean/fs/sysfs/Makefile 2005-06-17 15:48:29.000000000 -0400
+++ 2.6.13-rc5-mm1/fs/sysfs/Makefile 2005-08-29 17:13:59.000000000 -0400
@@ -2,5 +2,4 @@
# Makefile for the sysfs virtual filesystem
#
-obj-y := inode.o file.o dir.o symlink.o mount.o bin.o \
- group.o
+obj-y := sysfs.o
diff -up --recursive 2.6.13-rc5-mm1.clean/fs/sysfs/sysfs.c 2.6.13-rc5-mm1/fs/sysfs/sysfs.c
--- 2.6.13-rc5-mm1.clean/fs/sysfs/sysfs.c 2005-08-30 17:52:35.000000000 -0400
+++ 2.6.13-rc5-mm1/fs/sysfs/sysfs.c 2005-08-29 21:04:40.000000000 -0400
@@ -0,0 +1,1680 @@
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/backing-dev.h>
+#include <linux/pagemap.h>
+#include <linux/fsnotify.h>
+
+struct sysfs_symlink {
+ char *link_name;
+ struct kobject *sl_target;
+};
+
+static inline struct kobject *to_kobj(struct dentry *dentry)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ return ((struct kobject *)sd->s_element);
+}
+
+static inline struct attribute *to_attr(struct dentry *dentry)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ return ((struct attribute *)sd->s_element);
+}
+
+static inline struct kobject *sysfs_get_kobject(struct dentry *dentry)
+{
+ struct kobject *kobj = NULL;
+
+ spin_lock(&dcache_lock);
+ if (!d_unhashed(dentry)) {
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ if (sd->s_type & SYSFS_KOBJ_LINK) {
+ struct sysfs_symlink *sl = sd->s_element;
+ kobj = kobject_get(sl->sl_target);
+ } else
+ kobj = kobject_get(sd->s_element);
+ }
+ spin_unlock(&dcache_lock);
+
+ return kobj;
+}
+
+static kmem_cache_t *sysfs_dir_cachep;
+
+static void release_sysfs_dirent(struct sysfs_dirent *sd)
+{
+ if (sd->s_type & SYSFS_KOBJ_LINK) {
+ struct sysfs_symlink *sl = sd->s_element;
+ kfree(sl->link_name);
+ kobject_put(sl->sl_target);
+ kfree(sl);
+ }
+ kfree(sd->s_iattr);
+ kmem_cache_free(sysfs_dir_cachep, sd);
+}
+
+static struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd)
+{
+ if (sd) {
+ WARN_ON(!atomic_read(&sd->s_count));
+ atomic_inc(&sd->s_count);
+ }
+ return sd;
+}
+
+static void sysfs_put(struct sysfs_dirent *sd)
+{
+ WARN_ON(!atomic_read(&sd->s_count));
+ if (atomic_dec_and_test(&sd->s_count))
+ release_sysfs_dirent(sd);
+}
+
+/*
+ * inode.c - basic inode and dentry operations.
+ */
+
+int sysfs_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ struct inode *inode = dentry->d_inode;
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ struct iattr *sd_iattr;
+ unsigned int ia_valid = iattr->ia_valid;
+ int error;
+
+ if (!sd)
+ return -EINVAL;
+
+ sd_iattr = sd->s_iattr;
+
+ error = inode_change_ok(inode, iattr);
+ if (error)
+ return error;
+
+ error = inode_setattr(inode, iattr);
+ if (error)
+ return error;
+
+ if (!sd_iattr) {
+ /* setting attributes for the first time, allocate now */
+ sd_iattr = kmalloc(sizeof(struct iattr), GFP_KERNEL);
+ if (!sd_iattr)
+ return -ENOMEM;
+ /* assign default attributes */
+ memset(sd_iattr, 0, sizeof(struct iattr));
+ sd_iattr->ia_mode = sd->s_mode;
+ sd_iattr->ia_uid = 0;
+ sd_iattr->ia_gid = 0;
+ sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime =
+ CURRENT_TIME;
+ sd->s_iattr = sd_iattr;
+ }
+
+ /* attributes were changed atleast once in past */
+
+ if (ia_valid & ATTR_UID)
+ sd_iattr->ia_uid = iattr->ia_uid;
+ if (ia_valid & ATTR_GID)
+ sd_iattr->ia_gid = iattr->ia_gid;
+ if (ia_valid & ATTR_ATIME)
+ sd_iattr->ia_atime = timespec_trunc(iattr->ia_atime,
+ inode->i_sb->s_time_gran);
+ if (ia_valid & ATTR_MTIME)
+ sd_iattr->ia_mtime = timespec_trunc(iattr->ia_mtime,
+ inode->i_sb->s_time_gran);
+ if (ia_valid & ATTR_CTIME)
+ sd_iattr->ia_ctime = timespec_trunc(iattr->ia_ctime,
+ inode->i_sb->s_time_gran);
+ if (ia_valid & ATTR_MODE) {
+ umode_t mode = iattr->ia_mode;
+
+ if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
+ mode &= ~S_ISGID;
+ sd_iattr->ia_mode = sd->s_mode = mode;
+ }
+
+ return error;
+}
+
+static struct inode_operations sysfs_inode_operations = {
+ .setattr = sysfs_setattr,
+};
+
+static struct super_block *sysfs_sb;
+
+static struct address_space_operations sysfs_aops = {
+ .readpage = simple_readpage,
+ .prepare_write = simple_prepare_write,
+ .commit_write = simple_commit_write
+};
+
+static struct backing_dev_info sysfs_backing_dev_info = {
+ .ra_pages = 0, /* No readahead */
+ .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
+};
+
+static struct inode *sysfs_new_inode(mode_t mode, struct sysfs_dirent *sd)
+{
+ struct inode *inode = new_inode(sysfs_sb);
+ if (inode) {
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_blocks = 0;
+ inode->i_mapping->a_ops = &sysfs_aops;
+ inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
+ inode->i_op = &sysfs_inode_operations;
+
+ if (sd->s_iattr) {
+ /* sysfs_dirent has non-default attributes
+ * get them for the new inode from persistent copy
+ * in sysfs_dirent */
+ struct iattr *iattr = sd->s_iattr;
+ inode->i_mode = iattr->ia_mode;
+ inode->i_uid = iattr->ia_uid;
+ inode->i_gid = iattr->ia_gid;
+ inode->i_atime = iattr->ia_atime;
+ inode->i_mtime = iattr->ia_mtime;
+ inode->i_ctime = iattr->ia_ctime;
+ } else {
+ inode->i_mode = mode;
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ }
+ }
+ return inode;
+}
+
+static int sysfs_create(struct dentry *dentry, int mode, int (*init) (struct inode *))
+{
+ int error = 0;
+ struct inode *inode = NULL;
+ if (dentry) {
+ if (!dentry->d_inode) {
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ if ((inode = sysfs_new_inode(mode, sd))) {
+ if (dentry->d_parent
+ && dentry->d_parent->d_inode) {
+ struct inode *p_inode =
+ dentry->d_parent->d_inode;
+ p_inode->i_mtime = p_inode->i_ctime =
+ CURRENT_TIME;
+ }
+ goto Proceed;
+ } else
+ error = -ENOMEM;
+ } else
+ error = -EEXIST;
+ } else
+ error = -ENOENT;
+ goto Done;
+
+ Proceed:
+ if (init)
+ error = init(inode);
+ if (!error) {
+ d_instantiate(dentry, inode);
+ if (S_ISDIR(mode)) /* pin only directory dentry in core */
+ dget(dentry);
+ } else
+ iput(inode);
+ Done:
+ return error;
+}
+
+/*
+ * Get the name for corresponding element represented by the given sysfs_dirent
+ */
+static const unsigned char *sysfs_get_name(struct sysfs_dirent *sd)
+{
+ struct attribute *attr;
+ struct bin_attribute *bin_attr;
+ struct sysfs_symlink *sl;
+
+ if (!sd || !sd->s_element)
+ BUG();
+
+ switch (sd->s_type) {
+ case SYSFS_DIR:
+ /* Always have a dentry so use that */
+ return sd->s_dentry->d_name.name;
+
+ case SYSFS_KOBJ_ATTR:
+ attr = sd->s_element;
+ return attr->name;
+
+ case SYSFS_KOBJ_BIN_ATTR:
+ bin_attr = sd->s_element;
+ return bin_attr->attr.name;
+
+ case SYSFS_KOBJ_LINK:
+ sl = sd->s_element;
+ return sl->link_name;
+ }
+ return NULL;
+}
+
+/*
+ * Unhashes the dentry corresponding to given sysfs_dirent
+ * Called with parent inode's i_sem held.
+ */
+static void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent)
+{
+ struct dentry *dentry = sd->s_dentry;
+
+ if (dentry) {
+ spin_lock(&dcache_lock);
+ spin_lock(&dentry->d_lock);
+ if (!(d_unhashed(dentry) && dentry->d_inode)) {
+ dget_locked(dentry);
+ __d_drop(dentry);
+ spin_unlock(&dentry->d_lock);
+ spin_unlock(&dcache_lock);
+ simple_unlink(parent->d_inode, dentry);
+ } else {
+ spin_unlock(&dentry->d_lock);
+ spin_unlock(&dcache_lock);
+ }
+ }
+}
+
+static void sysfs_hash_and_remove(struct dentry *dir, const char *name)
+{
+ struct sysfs_dirent *sd;
+ struct sysfs_dirent *parent_sd = dir->d_fsdata;
+
+ down(&dir->d_inode->i_sem);
+ list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+ if (!sd->s_element)
+ continue;
+ if (!strcmp(sysfs_get_name(sd), name)) {
+ list_del_init(&sd->s_sibling);
+ sysfs_drop_dentry(sd, dir);
+ sysfs_put(sd);
+ break;
+ }
+ }
+ up(&dir->d_inode->i_sem);
+}
+
+/*
+ * symlink.c - operations for sysfs symlinks.
+ */
+
+static int object_depth(struct kobject *kobj)
+{
+ struct kobject *p = kobj;
+ int depth = 0;
+ do {
+ depth++;
+ } while ((p = p->parent));
+ return depth;
+}
+
+static int object_path_length(struct kobject *kobj)
+{
+ struct kobject *p = kobj;
+ int length = 1;
+ do {
+ length += strlen(kobject_name(p)) + 1;
+ p = p->parent;
+ } while (p);
+ return length;
+}
+
+static void fill_object_path(struct kobject *kobj, char *buffer, int length)
+{
+ struct kobject *p;
+
+ --length;
+ for (p = kobj; p; p = p->parent) {
+ int cur = strlen(kobject_name(p));
+
+ /* back up enough to print this bus id with '/' */
+ length -= cur;
+ strncpy(buffer + length, kobject_name(p), cur);
+ *(buffer + --length) = '/';
+ }
+}
+
+static int sysfs_get_target_path(struct kobject *kobj, struct kobject *target, char *path)
+{
+ char *s;
+ int depth, size;
+
+ depth = object_depth(kobj);
+ size = object_path_length(target) + depth * 3 - 1;
+ if (size > PATH_MAX)
+ return -ENAMETOOLONG;
+
+ pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size);
+
+ for (s = path; depth--; s += 3)
+ strcpy(s, "../");
+
+ fill_object_path(target, path, size);
+ pr_debug("%s: path = '%s'\n", __FUNCTION__, path);
+
+ return 0;
+}
+
+DECLARE_RWSEM(sysfs_rename_sem);
+
+static int sysfs_getlink(struct dentry *dentry, char *path)
+{
+ struct kobject *kobj, *sl_target;
+ int error = 0;
+
+ kobj = sysfs_get_kobject(dentry->d_parent);
+ if (!kobj)
+ return -EINVAL;
+
+ sl_target = sysfs_get_kobject(dentry);
+ if (!sl_target) {
+ kobject_put(kobj);
+ return -EINVAL;
+ }
+
+ down_read(&sysfs_rename_sem);
+ error = sysfs_get_target_path(kobj, sl_target, path);
+ up_read(&sysfs_rename_sem);
+
+ kobject_put(kobj);
+ kobject_put(sl_target);
+ return error;
+
+}
+
+static int sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ int error = -ENOMEM;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ if (page)
+ error = sysfs_getlink(dentry, (char *)page);
+ nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
+ return 0;
+}
+
+static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *page = nd_get_link(nd);
+ if (!IS_ERR(page))
+ free_page((unsigned long)page);
+}
+
+static struct inode_operations sysfs_symlink_inode_operations = {
+ .readlink = generic_readlink,
+ .follow_link = sysfs_follow_link,
+ .put_link = sysfs_put_link,
+};
+
+static int init_symlink(struct inode *inode)
+{
+ inode->i_op = &sysfs_symlink_inode_operations;
+ return 0;
+}
+
+/*
+ * file.c - operations for regular (text) files.
+ */
+
+#define to_subsys(k) container_of(k, struct subsystem, kset.kobj)
+#define to_sattr(a) container_of(a, struct subsys_attribute, attr)
+
+/*
+ * Subsystem file operations.
+ * These operations allow subsystems to have files that can be
+ * read/written.
+ */
+static ssize_t subsys_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *page)
+{
+ struct subsystem *s = to_subsys(kobj);
+ struct subsys_attribute *sattr = to_sattr(attr);
+ ssize_t ret = -EIO;
+
+ if (sattr->show)
+ ret = sattr->show(s, page);
+ return ret;
+}
+
+static ssize_t subsys_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *page, size_t count)
+{
+ struct subsystem *s = to_subsys(kobj);
+ struct subsys_attribute *sattr = to_sattr(attr);
+ ssize_t ret = -EIO;
+
+ if (sattr->store)
+ ret = sattr->store(s, page, count);
+ return ret;
+}
+
+static struct sysfs_ops subsys_sysfs_ops = {
+ .show = subsys_attr_show,
+ .store = subsys_attr_store,
+};
+
+/*
+ * file.c - operations for regular (text) files.
+ */
+
+struct sysfs_buffer {
+ size_t count;
+ loff_t pos;
+ char *page;
+ struct sysfs_ops *ops;
+ struct semaphore sem;
+ int needs_read_fill;
+};
+
+/*
+ * Allocate @buffer->page, if it hasn't been already, then call the
+ * kobject's show() method to fill the buffer with this attribute's
+ * data. This is called only once, on the file's first read.
+ */
+static int fill_read_buffer(struct dentry *dentry, struct sysfs_buffer *buffer)
+{
+ struct attribute *attr = to_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+ struct sysfs_ops *ops = buffer->ops;
+ int ret = 0;
+ ssize_t count;
+
+ if (!buffer->page)
+ buffer->page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!buffer->page)
+ return -ENOMEM;
+
+ count = ops->show(kobj, attr, buffer->page);
+ buffer->needs_read_fill = 0;
+ BUG_ON(count > (ssize_t) PAGE_SIZE);
+ if (count >= 0)
+ buffer->count = count;
+ else
+ ret = count;
+ return ret;
+}
+
+/*
+ * Copy the buffer we filled in fill_read_buffer() to userspace.
+ * This is done at the reader's leisure, copying and advancing
+ * the amount they specify each time.
+ * This may be called continuously until the buffer is empty.
+ */
+static int flush_read_buffer(struct sysfs_buffer *buffer, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int error;
+
+ if (*ppos > buffer->count)
+ return 0;
+
+ if (count > (buffer->count - *ppos))
+ count = buffer->count - *ppos;
+
+ error = copy_to_user(buf, buffer->page + *ppos, count);
+ if (!error)
+ *ppos += count;
+ return error ? -EFAULT : count;
+}
+
+/*
+ * Allocate @buffer->page if it hasn't been already, then
+ * copy the user-supplied buffer into it.
+ */
+static int fill_write_buffer(struct sysfs_buffer *buffer, const char __user *buf,
+ size_t count)
+{
+ int error;
+
+ if (!buffer->page)
+ buffer->page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!buffer->page)
+ return -ENOMEM;
+
+ if (count > PAGE_SIZE)
+ count = PAGE_SIZE;
+ error = copy_from_user(buffer->page, buf, count);
+ buffer->needs_read_fill = 1;
+ return error ? -EFAULT : count;
+}
+
+/*
+ * Get the correct pointers for the kobject and the attribute we're
+ * dealing with, then call the store method for the attribute,
+ * passing the buffer that we acquired in fill_write_buffer().
+ */
+static int flush_write_buffer(struct dentry *dentry, struct sysfs_buffer *buffer,
+ size_t count)
+{
+ struct attribute *attr = to_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+ struct sysfs_ops *ops = buffer->ops;
+
+ return ops->store(kobj, attr, buffer->page, count);
+}
+
+static int check_perm(struct inode *inode, struct file *file)
+{
+ struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent);
+ struct attribute *attr = to_attr(file->f_dentry);
+ struct sysfs_buffer *buffer;
+ struct sysfs_ops *ops = NULL;
+ int error = 0;
+
+ if (!kobj || !attr)
+ goto Einval;
+
+ /* Grab the module reference for this attribute if we have one */
+ if (!try_module_get(attr->owner)) {
+ error = -ENODEV;
+ goto Done;
+ }
+
+ /* if the kobject has no ktype, then we assume that it is a subsystem
+ * itself, and use ops for it.
+ */
+ if (kobj->kset && kobj->kset->ktype)
+ ops = kobj->kset->ktype->sysfs_ops;
+ else if (kobj->ktype)
+ ops = kobj->ktype->sysfs_ops;
+ else
+ ops = &subsys_sysfs_ops;
+
+ /* No sysfs operations, either from having no subsystem,
+ * or the subsystem have no operations.
+ */
+ if (!ops)
+ goto Eaccess;
+
+ /* File needs write support.
+ * The inode's perms must say it's ok, and we must have a store method.
+ */
+ if (file->f_mode & FMODE_WRITE) {
+
+ if (!(inode->i_mode & S_IWUGO) || !ops->store)
+ goto Eaccess;
+
+ }
+
+ /* File needs read support.
+ * The inode's perms must say it's ok, and we there
+ * must be a show method for it.
+ */
+ if (file->f_mode & FMODE_READ) {
+ if (!(inode->i_mode & S_IRUGO) || !ops->show)
+ goto Eaccess;
+ }
+
+ /* No error? Great, allocate a buffer for the file, and store it
+ * it in file->private_data for easy access.
+ */
+ buffer = kmalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);
+ if (buffer) {
+ memset(buffer, 0, sizeof(struct sysfs_buffer));
+ init_MUTEX(&buffer->sem);
+ buffer->needs_read_fill = 1;
+ buffer->ops = ops;
+ file->private_data = buffer;
+ } else
+ error = -ENOMEM;
+ goto Done;
+
+ Einval:
+ error = -EINVAL;
+ goto Done;
+ Eaccess:
+ error = -EACCES;
+ module_put(attr->owner);
+ Done:
+ if (error && kobj)
+ kobject_put(kobj);
+ return error;
+}
+
+/*
+ * Userspace wants to read an attribute file. The attribute descriptor
+ * is in the file's ->d_fsdata. The target object is in the directory's
+ * ->d_fsdata.
+ *
+ * We call fill_read_buffer() to allocate and fill the buffer from the
+ * object's show() method exactly once (if the read is happening from
+ * the beginning of the file). That should fill the entire buffer with
+ * all the data the object has to offer for that attribute.
+ * We then call flush_read_buffer() to copy the buffer to userspace
+ * in the increments specified.
+ */
+static ssize_t sysfs_read_file(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct sysfs_buffer *buffer = file->private_data;
+ ssize_t retval = 0;
+
+ down(&buffer->sem);
+ if (buffer->needs_read_fill) {
+ if ((retval = fill_read_buffer(file->f_dentry, buffer)))
+ goto out;
+ }
+ pr_debug("%s: count = %d, ppos = %lld, buf = %s\n",
+ __FUNCTION__, count, *ppos, buffer->page);
+ retval = flush_read_buffer(buffer, buf, count, ppos);
+ out:
+ up(&buffer->sem);
+ return retval;
+}
+
+/*
+ * Similar to sysfs_read_file(), though working in the opposite direction.
+ * We allocate and fill the data from the user in fill_write_buffer(),
+ * then push it to the kobject in flush_write_buffer().
+ * There is no easy way for us to know if userspace is only doing a partial
+ * write, so we don't support them. We expect the entire buffer to come
+ * on the first write.
+ * Hint: if you're writing a value, first read the file, modify only the
+ * the value you're changing, then write entire buffer back.
+ */
+static ssize_t sysfs_write_file(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct sysfs_buffer *buffer = file->private_data;
+
+ down(&buffer->sem);
+ count = fill_write_buffer(buffer, buf, count);
+ if (count > 0)
+ count = flush_write_buffer(file->f_dentry, buffer, count);
+ if (count > 0)
+ *ppos += count;
+ up(&buffer->sem);
+ return count;
+}
+
+static int sysfs_open_file(struct inode *inode, struct file *filp)
+{
+ return check_perm(inode, filp);
+}
+
+static int sysfs_release(struct inode *inode, struct file *filp)
+{
+ struct kobject *kobj = to_kobj(filp->f_dentry->d_parent);
+ struct attribute *attr = to_attr(filp->f_dentry);
+ struct module *owner = attr->owner;
+ struct sysfs_buffer *buffer = filp->private_data;
+
+ if (kobj)
+ kobject_put(kobj);
+ /* After this point, attr should not be accessed. */
+ module_put(owner);
+
+ if (buffer) {
+ if (buffer->page)
+ free_page((unsigned long)buffer->page);
+ kfree(buffer);
+ }
+ return 0;
+}
+
+static struct file_operations sysfs_file_operations = {
+ .read = sysfs_read_file,
+ .write = sysfs_write_file,
+ .llseek = generic_file_llseek,
+ .open = sysfs_open_file,
+ .release = sysfs_release,
+};
+
+static int init_file(struct inode *inode)
+{
+ inode->i_size = PAGE_SIZE;
+ inode->i_fop = &sysfs_file_operations;
+ return 0;
+}
+
+static void sysfs_d_iput(struct dentry *dentry, struct inode *inode)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+
+ if (sd) {
+ BUG_ON(sd->s_dentry != dentry);
+ sd->s_dentry = NULL;
+ sysfs_put(sd);
+ }
+ iput(inode);
+}
+
+static struct dentry_operations sysfs_dentry_ops = {
+ .d_iput = sysfs_d_iput,
+};
+
+/*
+ * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent
+ */
+static struct sysfs_dirent *sysfs_new_dirent(struct sysfs_dirent *parent_sd,
+ void *element)
+{
+ struct sysfs_dirent *sd;
+
+ sd = kmem_cache_alloc(sysfs_dir_cachep, GFP_KERNEL);
+ if (!sd)
+ return NULL;
+
+ memset(sd, 0, sizeof(*sd));
+ atomic_set(&sd->s_count, 1);
+ INIT_LIST_HEAD(&sd->s_children);
+ list_add(&sd->s_sibling, &parent_sd->s_children);
+ sd->s_element = element;
+
+ return sd;
+}
+
+int sysfs_make_dirent(struct sysfs_dirent *parent_sd, struct dentry *dentry,
+ void *element, umode_t mode, int type)
+{
+ struct sysfs_dirent *sd;
+
+ sd = sysfs_new_dirent(parent_sd, element);
+ if (!sd)
+ return -ENOMEM;
+
+ sd->s_mode = mode;
+ sd->s_type = type;
+ sd->s_dentry = dentry;
+ if (dentry) {
+ dentry->d_fsdata = sysfs_get(sd);
+ dentry->d_op = &sysfs_dentry_ops;
+ }
+
+ return 0;
+}
+
+static int sysfs_add_file(struct dentry *dir, const struct attribute *attr, int type)
+{
+ struct sysfs_dirent *parent_sd = dir->d_fsdata;
+ umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG;
+ int error = 0;
+
+ down(&dir->d_inode->i_sem);
+ error = sysfs_make_dirent(parent_sd, NULL, (void *)attr, mode, type);
+ up(&dir->d_inode->i_sem);
+
+ return error;
+}
+
+int sysfs_create_file(struct kobject *kobj, const struct attribute *attr)
+{
+ BUG_ON(!kobj || !kobj->dentry || !attr);
+
+ return sysfs_add_file(kobj->dentry, attr, SYSFS_KOBJ_ATTR);
+}
+
+static int sysfs_add_link(struct dentry *parent, const char *name, struct kobject *target)
+{
+ struct sysfs_dirent *parent_sd = parent->d_fsdata;
+ struct sysfs_symlink *sl;
+ int error = 0;
+
+ error = -ENOMEM;
+ sl = kmalloc(sizeof(*sl), GFP_KERNEL);
+ if (!sl)
+ goto exit1;
+
+ sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL);
+ if (!sl->link_name)
+ goto exit2;
+
+ strcpy(sl->link_name, name);
+ sl->sl_target = kobject_get(target);
+
+ error = sysfs_make_dirent(parent_sd, NULL, sl, S_IFLNK | S_IRWXUGO,
+ SYSFS_KOBJ_LINK);
+ if (!error)
+ return 0;
+
+ kfree(sl->link_name);
+ exit2:
+ kfree(sl);
+ exit1:
+ return error;
+}
+
+/*
+ * dir.c - Operations for directories.
+ */
+
+static int sysfs_dir_open(struct inode *inode, struct file *file)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct sysfs_dirent *parent_sd = dentry->d_fsdata;
+
+ down(&dentry->d_inode->i_sem);
+ file->private_data = sysfs_new_dirent(parent_sd, NULL);
+ up(&dentry->d_inode->i_sem);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static int sysfs_dir_close(struct inode *inode, struct file *file)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct sysfs_dirent *cursor = file->private_data;
+
+ down(&dentry->d_inode->i_sem);
+ list_del_init(&cursor->s_sibling);
+ up(&dentry->d_inode->i_sem);
+
+ release_sysfs_dirent(cursor);
+
+ return 0;
+}
+
+/* Relationship between s_mode and the DT_xxx types */
+static inline unsigned char dt_type(struct sysfs_dirent *sd)
+{
+ return (sd->s_mode >> 12) & 15;
+}
+
+static int sysfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct sysfs_dirent *parent_sd = dentry->d_fsdata;
+ struct sysfs_dirent *cursor = filp->private_data;
+ struct list_head *p, *q = &cursor->s_sibling;
+ ino_t ino;
+ int i = filp->f_pos;
+
+ switch (i) {
+ case 0:
+ ino = dentry->d_inode->i_ino;
+ if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
+ break;
+ filp->f_pos++;
+ i++;
+ /* fallthrough */
+ case 1:
+ ino = parent_ino(dentry);
+ if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
+ break;
+ filp->f_pos++;
+ i++;
+ /* fallthrough */
+ default:
+ if (filp->f_pos == 2) {
+ list_del(q);
+ list_add(q, &parent_sd->s_children);
+ }
+ for (p = q->next; p != &parent_sd->s_children; p = p->next) {
+ struct sysfs_dirent *next;
+ const char *name;
+ int len;
+
+ next = list_entry(p, struct sysfs_dirent, s_sibling);
+ if (!next->s_element)
+ continue;
+
+ name = sysfs_get_name(next);
+ len = strlen(name);
+ if (next->s_dentry)
+ ino = next->s_dentry->d_inode->i_ino;
+ else
+ ino = iunique(sysfs_sb, 2);
+
+ if (filldir(dirent, name, len, filp->f_pos, ino,
+ dt_type(next)) < 0)
+ return 0;
+
+ list_del(q);
+ list_add(q, p);
+ p = q;
+ filp->f_pos++;
+ }
+ }
+ return 0;
+}
+
+static loff_t sysfs_dir_lseek(struct file *file, loff_t offset, int origin)
+{
+ struct dentry *dentry = file->f_dentry;
+
+ down(&dentry->d_inode->i_sem);
+ switch (origin) {
+ case 1:
+ offset += file->f_pos;
+ case 0:
+ if (offset >= 0)
+ break;
+ default:
+ up(&file->f_dentry->d_inode->i_sem);
+ return -EINVAL;
+ }
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ if (file->f_pos >= 2) {
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ struct sysfs_dirent *cursor = file->private_data;
+ struct list_head *p;
+ loff_t n = file->f_pos - 2;
+
+ list_del(&cursor->s_sibling);
+ p = sd->s_children.next;
+ while (n && p != &sd->s_children) {
+ struct sysfs_dirent *next;
+ next = list_entry(p, struct sysfs_dirent,
+ s_sibling);
+ if (next->s_element)
+ n--;
+ p = p->next;
+ }
+ list_add_tail(&cursor->s_sibling, p);
+ }
+ }
+ up(&dentry->d_inode->i_sem);
+ return offset;
+}
+
+static struct file_operations sysfs_dir_operations = {
+ .open = sysfs_dir_open,
+ .release = sysfs_dir_close,
+ .llseek = sysfs_dir_lseek,
+ .read = generic_read_dir,
+ .readdir = sysfs_readdir,
+};
+
+/*
+ * bin.c - binary file operations for sysfs.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Matthew Wilcox
+ * Copyright (c) 2004 Silicon Graphics, Inc.
+ */
+
+static inline struct bin_attribute *to_bin_attr(struct dentry *dentry)
+{
+ struct sysfs_dirent *sd = dentry->d_fsdata;
+ return ((struct bin_attribute *)sd->s_element);
+}
+
+static int fill_read(struct dentry *dentry, char *buffer, loff_t off,
+ size_t count)
+{
+ struct bin_attribute *attr = to_bin_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+
+ if (!attr->read)
+ return -EIO;
+
+ return attr->read(kobj, buffer, off, count);
+}
+
+static ssize_t read(struct file *file, char __user *userbuf, size_t count, loff_t *off)
+{
+ char *buffer = file->private_data;
+ struct dentry *dentry = file->f_dentry;
+ int size = dentry->d_inode->i_size;
+ loff_t offs = *off;
+ int ret;
+
+ if (count > PAGE_SIZE)
+ count = PAGE_SIZE;
+
+ if (size) {
+ if (offs > size)
+ return 0;
+ if (offs + count > size)
+ count = size - offs;
+ }
+
+ ret = fill_read(dentry, buffer, offs, count);
+ if (ret < 0)
+ return ret;
+ count = ret;
+
+ if (copy_to_user(userbuf, buffer, count))
+ return -EFAULT;
+
+ pr_debug("offs = %lld, *off = %lld, count = %zd\n", offs, *off, count);
+
+ *off = offs + count;
+
+ return count;
+}
+
+static int flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
+{
+ struct bin_attribute *attr = to_bin_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+
+ if (!attr->write)
+ return -EIO;
+
+ return attr->write(kobj, buffer, offset, count);
+}
+
+static ssize_t write(struct file *file, const char __user *userbuf, size_t count, loff_t *off)
+{
+ char *buffer = file->private_data;
+ struct dentry *dentry = file->f_dentry;
+ int size = dentry->d_inode->i_size;
+ loff_t offs = *off;
+
+ if (count > PAGE_SIZE)
+ count = PAGE_SIZE;
+ if (size) {
+ if (offs > size)
+ return 0;
+ if (offs + count > size)
+ count = size - offs;
+ }
+
+ if (copy_from_user(buffer, userbuf, count))
+ return -EFAULT;
+
+ count = flush_write(dentry, buffer, offs, count);
+ if (count > 0)
+ *off = offs + count;
+ return count;
+}
+
+static int mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct bin_attribute *attr = to_bin_attr(dentry);
+ struct kobject *kobj = to_kobj(dentry->d_parent);
+
+ if (!attr->mmap)
+ return -EINVAL;
+
+ return attr->mmap(kobj, attr, vma);
+}
+
+static int open(struct inode *inode, struct file *file)
+{
+ struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent);
+ struct bin_attribute *attr = to_bin_attr(file->f_dentry);
+ int error = -EINVAL;
+
+ if (!kobj || !attr)
+ goto Done;
+
+ /* Grab the module reference for this attribute if we have one */
+ error = -ENODEV;
+ if (!try_module_get(attr->attr.owner))
+ goto Done;
+
+ error = -EACCES;
+ if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap))
+ goto Error;
+ if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap))
+ goto Error;
+
+ error = -ENOMEM;
+ file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!file->private_data)
+ goto Error;
+
+ error = 0;
+ goto Done;
+
+ Error:
+ module_put(attr->attr.owner);
+ Done:
+ if (error && kobj)
+ kobject_put(kobj);
+ return error;
+}
+
+static int release(struct inode *inode, struct file *file)
+{
+ struct kobject *kobj = to_kobj(file->f_dentry->d_parent);
+ struct bin_attribute *attr = to_bin_attr(file->f_dentry);
+ u8 *buffer = file->private_data;
+
+ if (kobj)
+ kobject_put(kobj);
+ module_put(attr->attr.owner);
+ kfree(buffer);
+ return 0;
+}
+
+struct file_operations bin_fops = {
+ .read = read,
+ .write = write,
+ .mmap = mmap,
+ .llseek = generic_file_llseek,
+ .open = open,
+ .release = release,
+};
+
+int sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr)
+{
+ BUG_ON(!kobj || !kobj->dentry || !attr);
+
+ return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
+}
+
+int sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr)
+{
+ sysfs_hash_and_remove(kobj->dentry, attr->attr.name);
+ return 0;
+}
+
+/* attaches attribute's sysfs_dirent to the dentry corresponding to the
+ * attribute file
+ */
+static int sysfs_attach_attr(struct sysfs_dirent *sd, struct dentry *dentry)
+{
+ struct attribute *attr = NULL;
+ struct bin_attribute *bin_attr = NULL;
+ int (*init) (struct inode *) = NULL;
+ int error = 0;
+
+ if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) {
+ bin_attr = sd->s_element;
+ attr = &bin_attr->attr;
+ } else {
+ attr = sd->s_element;
+ init = init_file;
+ }
+
+ dentry->d_fsdata = sysfs_get(sd);
+ sd->s_dentry = dentry;
+ error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init);
+ if (error) {
+ sysfs_put(sd);
+ return error;
+ }
+
+ if (bin_attr) {
+ dentry->d_inode->i_size = bin_attr->size;
+ dentry->d_inode->i_fop = &bin_fops;
+ }
+ dentry->d_op = &sysfs_dentry_ops;
+ d_rehash(dentry);
+
+ return 0;
+}
+
+static int sysfs_attach_link(struct sysfs_dirent *sd, struct dentry *dentry)
+{
+ int err = 0;
+
+ dentry->d_fsdata = sysfs_get(sd);
+ sd->s_dentry = dentry;
+ err = sysfs_create(dentry, S_IFLNK | S_IRWXUGO, init_symlink);
+ if (!err) {
+ dentry->d_op = &sysfs_dentry_ops;
+ d_rehash(dentry);
+ } else
+ sysfs_put(sd);
+
+ return err;
+}
+
+static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;
+ struct sysfs_dirent *sd;
+ int err = 0;
+
+ list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ const unsigned char *name = sysfs_get_name(sd);
+
+ if (strcmp(name, dentry->d_name.name))
+ continue;
+
+ if (sd->s_type & SYSFS_KOBJ_LINK)
+ err = sysfs_attach_link(sd, dentry);
+ else
+ err = sysfs_attach_attr(sd, dentry);
+ break;
+ }
+ }
+
+ return ERR_PTR(err);
+}
+
+struct inode_operations sysfs_dir_inode_operations = {
+ .lookup = sysfs_lookup,
+ .setattr = sysfs_setattr,
+};
+
+static int init_dir(struct inode *inode)
+{
+ inode->i_op = &sysfs_dir_inode_operations;
+ inode->i_fop = &sysfs_dir_operations;
+
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inode->i_nlink++;
+ return 0;
+}
+
+static int create_dir(struct kobject *k, struct dentry *p, const char *n, struct dentry **d)
+{
+ int error;
+ umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
+
+ down(&p->d_inode->i_sem);
+ *d = lookup_one_len(n, p, strlen(n));
+ if (!IS_ERR(*d)) {
+ error = sysfs_make_dirent(p->d_fsdata, *d, k, mode, SYSFS_DIR);
+ if (!error) {
+ error = sysfs_create(*d, mode, init_dir);
+ if (!error) {
+ p->d_inode->i_nlink++;
+ (*d)->d_op = &sysfs_dentry_ops;
+ d_rehash(*d);
+ }
+ }
+ if (error && (error != -EEXIST)) {
+ sysfs_put((*d)->d_fsdata);
+ d_drop(*d);
+ }
+ dput(*d);
+ } else
+ error = PTR_ERR(*d);
+ up(&p->d_inode->i_sem);
+ return error;
+}
+
+int sysfs_create_subdir(struct kobject *k, const char *n, struct dentry **d)
+{
+ return create_dir(k, k->dentry, n, d);
+}
+
+struct vfsmount *sysfs_mount;
+
+int sysfs_create_dir(struct kobject *kobj)
+{
+ struct dentry *dentry = NULL;
+ struct dentry *parent;
+ int error = 0;
+
+ BUG_ON(!kobj);
+
+ if (kobj->parent)
+ parent = kobj->parent->dentry;
+ else if (sysfs_mount && sysfs_mount->mnt_sb)
+ parent = sysfs_mount->mnt_sb->s_root;
+ else
+ return -EFAULT;
+
+ error = create_dir(kobj, parent, kobject_name(kobj), &dentry);
+ if (!error)
+ kobj->dentry = dentry;
+ return error;
+}
+
+static void remove_dir(struct dentry *d)
+{
+ struct dentry *parent = dget(d->d_parent);
+ struct sysfs_dirent *sd;
+
+ down(&parent->d_inode->i_sem);
+ d_delete(d);
+ sd = d->d_fsdata;
+ list_del_init(&sd->s_sibling);
+ sysfs_put(sd);
+ if (d->d_inode)
+ simple_rmdir(parent->d_inode, d);
+
+ pr_debug(" o %s removing done (%d)\n", d->d_name.name, atomic_read(&d->d_count));
+
+ up(&parent->d_inode->i_sem);
+ dput(parent);
+}
+
+void sysfs_remove_subdir(struct dentry *d)
+{
+ remove_dir(d);
+}
+
+/*
+ * sysfs_remove_dir - remove a kobject's directory.
+ * @kobj: object.
+ *
+ * The only thing special about this is that we remove any files in
+ * the directory before we remove the directory.
+ */
+void sysfs_remove_dir(struct kobject *kobj)
+{
+ struct dentry *dentry = dget(kobj->dentry);
+ struct sysfs_dirent *parent_sd;
+ struct sysfs_dirent *sd, *tmp;
+
+ if (!dentry)
+ return;
+
+ pr_debug("sysfs %s: removing dir\n", dentry->d_name.name);
+ down(&dentry->d_inode->i_sem);
+ parent_sd = dentry->d_fsdata;
+ list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
+ if (!sd->s_element || !(sd->s_type & SYSFS_NOT_PINNED))
+ continue;
+ list_del_init(&sd->s_sibling);
+ sysfs_drop_dentry(sd, dentry);
+ sysfs_put(sd);
+ }
+ up(&dentry->d_inode->i_sem);
+
+ remove_dir(dentry);
+ /* Drop reference from dget() on entrance. */
+ dput(dentry);
+}
+
+int sysfs_rename_dir(struct kobject *kobj, const char *new_name)
+{
+ int error = 0;
+ struct dentry *new_dentry, *parent;
+
+ if (!strcmp(kobject_name(kobj), new_name))
+ return -EINVAL;
+
+ if (!kobj->parent)
+ return -EINVAL;
+
+ down_write(&sysfs_rename_sem);
+ parent = kobj->parent->dentry;
+
+ down(&parent->d_inode->i_sem);
+
+ new_dentry = lookup_one_len(new_name, parent, strlen(new_name));
+ if (!IS_ERR(new_dentry)) {
+ if (!new_dentry->d_inode) {
+ error = kobject_set_name(kobj, "%s", new_name);
+ if (!error) {
+ d_add(new_dentry, NULL);
+ d_move(kobj->dentry, new_dentry);
+ } else
+ d_drop(new_dentry);
+ } else
+ error = -EEXIST;
+ dput(new_dentry);
+ }
+ up(&parent->d_inode->i_sem);
+ up_write(&sysfs_rename_sem);
+
+ return error;
+}
+
+/**
+ * sysfs_update_file - update the modified timestamp on an object attribute.
+ * @kobj: object we're acting for.
+ * @attr: attribute descriptor.
+ */
+int sysfs_update_file(struct kobject *kobj, const struct attribute *attr)
+{
+ struct dentry *dir = kobj->dentry;
+ struct dentry *victim;
+ int res = -ENOENT;
+
+ down(&dir->d_inode->i_sem);
+ victim = lookup_one_len(attr->name, dir, strlen(attr->name));
+ if (!IS_ERR(victim)) {
+ /* make sure dentry is really there */
+ if (victim->d_inode &&
+ (victim->d_parent->d_inode == dir->d_inode)) {
+ victim->d_inode->i_mtime = CURRENT_TIME;
+ fsnotify_modify(victim);
+
+ /**
+ * Drop reference from initial sysfs_get_dentry().
+ */
+ dput(victim);
+ res = 0;
+ } else
+ d_drop(victim);
+
+ /**
+ * Drop the reference acquired from sysfs_get_dentry() above.
+ */
+ dput(victim);
+ }
+ up(&dir->d_inode->i_sem);
+
+ return res;
+}
+
+/**
+ * sysfs_chmod_file - update the modified mode value on an object attribute.
+ * @kobj: object we're acting for.
+ * @attr: attribute descriptor.
+ * @mode: file permissions.
+ *
+ */
+int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode)
+{
+ struct dentry *dir = kobj->dentry;
+ struct dentry *victim;
+ struct inode *inode;
+ struct iattr newattrs;
+ int res = -ENOENT;
+
+ down(&dir->d_inode->i_sem);
+ victim = lookup_one_len(attr->name, dir, strlen(attr->name));
+ if (!IS_ERR(victim)) {
+ if (victim->d_inode &&
+ (victim->d_parent->d_inode == dir->d_inode)) {
+ inode = victim->d_inode;
+ down(&inode->i_sem);
+ newattrs.ia_mode = (mode & S_IALLUGO) |
+ (inode->i_mode & ~S_IALLUGO);
+ newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+ res = notify_change(victim, &newattrs);
+ up(&inode->i_sem);
+ }
+ dput(victim);
+ }
+ up(&dir->d_inode->i_sem);
+
+ return res;
+}
+
+/**
+ * sysfs_remove_file - remove an object attribute.
+ * @kobj: object we're acting for.
+ * @attr: attribute descriptor.
+ *
+ * Hash the attribute name and kill the victim.
+ */
+void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr)
+{
+ sysfs_hash_and_remove(kobj->dentry, attr->name);
+}
+
+/*
+ * fs/sysfs/group.c - Operations for adding/removing multiple files at once.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ *
+ * This file is released undert the GPL v2.
+ *
+ */
+static void remove_files(struct dentry *dir, const struct attribute_group *grp)
+{
+ struct attribute *const *attr;
+
+ for (attr = grp->attrs; *attr; attr++)
+ sysfs_hash_and_remove(dir, (*attr)->name);
+}
+
+static int create_files(struct dentry *dir, const struct attribute_group *grp)
+{
+ struct attribute *const *attr;
+ int error = 0;
+
+ for (attr = grp->attrs; *attr && !error; attr++) {
+ error = sysfs_add_file(dir, *attr, SYSFS_KOBJ_ATTR);
+ }
+ if (error)
+ remove_files(dir, grp);
+ return error;
+}
+
+int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
+{
+ struct dentry *dir;
+ int error;
+
+ BUG_ON(!kobj || !kobj->dentry);
+
+ if (grp->name) {
+ error = sysfs_create_subdir(kobj, grp->name, &dir);
+ if (error)
+ return error;
+ } else
+ dir = kobj->dentry;
+ dir = dget(dir);
+ if ((error = create_files(dir, grp))) {
+ if (grp->name)
+ sysfs_remove_subdir(dir);
+ }
+ dput(dir);
+ return error;
+}
+
+void sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp)
+{
+ struct dentry *dir;
+
+ if (grp->name)
+ dir = lookup_one_len(grp->name, kobj->dentry,
+ strlen(grp->name));
+ else
+ dir = dget(kobj->dentry);
+
+ remove_files(dir, grp);
+ if (grp->name)
+ sysfs_remove_subdir(dir);
+ /* release the ref. taken in this routine */
+ dput(dir);
+}
+
+/*
+ * mount.c - operations for initializing and mounting
+ */
+
+#define SYSFS_MAGIC 0x62656572
+
+static struct super_operations sysfs_ops = {
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
+};
+
+static struct sysfs_dirent sysfs_root = {
+ .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling),
+ .s_children = LIST_HEAD_INIT(sysfs_root.s_children),
+ .s_type = SYSFS_ROOT,
+};
+
+static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct inode *inode;
+ struct dentry *root;
+
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = SYSFS_MAGIC;
+ sb->s_op = &sysfs_ops;
+ sb->s_time_gran = 1;
+ sysfs_sb = sb;
+
+ inode = sysfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, &sysfs_root);
+ if (inode) {
+ inode->i_op = &sysfs_dir_inode_operations;
+ inode->i_fop = &sysfs_dir_operations;
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inode->i_nlink++;
+ } else {
+ pr_debug("sysfs: could not get root inode\n");
+ return -ENOMEM;
+ }
+
+ root = d_alloc_root(inode);
+ if (!root) {
+ pr_debug("%s: could not get root dentry!\n", __FUNCTION__);
+ iput(inode);
+ return -ENOMEM;
+ }
+ root->d_fsdata = &sysfs_root;
+ sb->s_root = root;
+ return 0;
+}
+
+static struct super_block *sysfs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return get_sb_single(fs_type, flags, data, sysfs_fill_super);
+}
+
+static struct file_system_type sysfs_fs_type = {
+ .name = "sysfs",
+ .get_sb = sysfs_get_sb,
+ .kill_sb = kill_litter_super,
+};
+
+int __init sysfs_init(void)
+{
+ int err = -ENOMEM;
+
+ sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",
+ sizeof(struct sysfs_dirent), 0, 0, NULL, NULL);
+ if (!sysfs_dir_cachep)
+ goto out;
+
+ err = register_filesystem(&sysfs_fs_type);
+ if (err)
+ goto out_err;
+ sysfs_mount = kern_mount(&sysfs_fs_type);
+ if (IS_ERR(sysfs_mount)) {
+ printk(KERN_ERR "sysfs: could not mount!\n");
+ err = PTR_ERR(sysfs_mount);
+ sysfs_mount = NULL;
+ unregister_filesystem(&sysfs_fs_type);
+ goto out_err;
+ }
+ out:
+ return err;
+ out_err:
+ kmem_cache_destroy(sysfs_dir_cachep);
+ sysfs_dir_cachep = NULL;
+ goto out;
+}
+
+/*
+ * sysfs_create_link - create symlink between two objects.
+ * @kobj: object whose directory we're creating the link in.
+ * @target: object we're pointing to.
+ * @name: name of the symlink.
+ */
+int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name)
+{
+ struct dentry *dentry = kobj->dentry;
+ int error = 0;
+
+ BUG_ON(!kobj || !kobj->dentry || !name);
+
+ down(&dentry->d_inode->i_sem);
+ error = sysfs_add_link(dentry, name, target);
+ up(&dentry->d_inode->i_sem);
+ return error;
+}
+
+void sysfs_remove_link(struct kobject *kobj, const char *name)
+{
+ sysfs_hash_and_remove(kobj->dentry, name);
+}
+
+EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
+EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
+EXPORT_SYMBOL_GPL(sysfs_create_dir);
+EXPORT_SYMBOL_GPL(sysfs_remove_dir);
+EXPORT_SYMBOL_GPL(sysfs_rename_dir);
+EXPORT_SYMBOL_GPL(sysfs_chmod_file);
+EXPORT_SYMBOL_GPL(sysfs_create_file);
+EXPORT_SYMBOL_GPL(sysfs_remove_file);
+EXPORT_SYMBOL_GPL(sysfs_update_file);
+EXPORT_SYMBOL_GPL(sysfs_create_group);
+EXPORT_SYMBOL_GPL(sysfs_remove_group);
+EXPORT_SYMBOL_GPL(sysfs_create_link);
+EXPORT_SYMBOL_GPL(sysfs_remove_link);
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-08-30 23:13 ` [RFC][PATCH 1 " Joel Becker
@ 2005-08-30 23:25 ` Daniel Phillips
2005-08-30 23:35 ` Daniel Phillips
2005-08-30 23:28 ` Andrew Morton
1 sibling, 1 reply; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 23:25 UTC (permalink / raw)
To: Joel Becker; +Cc: linux-kernel, Andrew Morton, Greg KH
On Wednesday 31 August 2005 09:13, Joel Becker wrote:
> On Wed, Aug 31, 2005 at 08:54:39AM +1000, Daniel Phillips wrote:
> > But it would be stupid to forbid users from creating directories in sysfs
> > or to forbid kernel modules from directly tweaking a configfs namespace.
> > Why should the kernel not be able to add objects to a directory a user
> > created? It should be up to the module author to decide these things.
>
> This is precisely why configfs is separate from sysfs. If both
> user and kernel can create objects, the lifetime of the object and its
> filesystem representation is very complex. Sysfs already has problems
> with people getting this wrong. configfs does not.
Could you please give a specific case?
> The fact that sysfs and configfs have similar backing stores
> does not make them the same thing.
It is not just the backing store, it is most of the code, all the structures,
most of the functionality, a good deal of the bugs...
Regards,
Daniel
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-08-30 23:13 ` [RFC][PATCH 1 " Joel Becker
2005-08-30 23:25 ` Daniel Phillips
@ 2005-08-30 23:28 ` Andrew Morton
2005-08-30 23:34 ` viro
` (3 more replies)
1 sibling, 4 replies; 22+ messages in thread
From: Andrew Morton @ 2005-08-30 23:28 UTC (permalink / raw)
To: Joel Becker; +Cc: phillips, linux-kernel, greg
Joel Becker <Joel.Becker@oracle.com> wrote:
>
> On Wed, Aug 31, 2005 at 08:54:39AM +1000, Daniel Phillips wrote:
> > But it would be stupid to forbid users from creating directories in sysfs or
> > to forbid kernel modules from directly tweaking a configfs namespace. Why
> > should the kernel not be able to add objects to a directory a user created?
> > It should be up to the module author to decide these things.
>
> This is precisely why configfs is separate from sysfs. If both
> user and kernel can create objects, the lifetime of the object and its
> filesystem representation is very complex. Sysfs already has problems
> with people getting this wrong. configfs does not.
> The fact that sysfs and configfs have similar backing stores
> does not make them the same thing.
>
Sure, but all that copying-and-pasting really sucks. I'm sure there's some
way of providing the slightly different semantics from the same codebase?
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 4 of 4] Configfs is really sysfs
2005-08-30 23:03 ` [RFC][PATCH 4 " Daniel Phillips
@ 2005-08-30 23:30 ` Daniel Phillips
0 siblings, 0 replies; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 23:30 UTC (permalink / raw)
To: linux-kernel; +Cc: Andrew Morton, Joel Becker, Greg KH
(without kmail bugs this time)
A kernel code example that uses the modified configfs to define a simple
configuration interface. Note the use of kobjects and ksets instead of
config_items and config_groups.
Also notice how much code is required to get a simple value from
userspace to kernel space. This is a big problem that needs to be
addressed soon, before we end up with tens or hundreds of thousands of
lines of code code bloat just to get and set variables from user space.
Regards,
Daniel
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/configfs.h>
struct ddbond_info {
struct kobject item;
int controlsock;
};
static inline struct ddbond_info *to_ddbond_info(struct kobject *item)
{
return container_of(item, struct ddbond_info, item);
}
static ssize_t ddbond_info_attr_show(struct kobject *item,
struct attribute *attr, char *page)
{
ssize_t count;
struct ddbond_info *ddbond_info = to_ddbond_info(item);
count = sprintf(page, "%d\n", ddbond_info->controlsock);
return count;
}
static ssize_t ddbond_info_attr_store(struct kobject *item,
struct attribute *attr, const char *page, size_t count)
{
struct ddbond_info *ddbond_info = to_ddbond_info(item);
unsigned long tmp;
char *p = (char *)page;
tmp = simple_strtoul(p, &p, 10);
if (!p || (*p && (*p != '\n')))
return -EINVAL;
if (tmp > INT_MAX)
return -ERANGE;
ddbond_info->controlsock = tmp;
return count;
}
static void ddbond_info_release(struct kobject *item)
{
kfree(to_ddbond_info(item));
}
static struct kobj_type ddbond_info_type = {
.sysfs_ops = &(struct sysfs_ops){
.show = ddbond_info_attr_show,
.store = ddbond_info_attr_store,
.release = ddbond_info_release,
},
.default_attrs = (struct attribute *[]){
&(struct attribute){
.owner = THIS_MODULE,
.name = "sockname",
.mode = S_IRUGO | S_IWUSR,
},
NULL,
},
.ct_owner = THIS_MODULE,
};
static struct kobject *ddbond_make_item(struct kset *group, const char *name)
{
struct ddbond_info *ddbond_info;
if (!(ddbond_info = kcalloc(1, sizeof(struct ddbond_info), GFP_KERNEL)))
return NULL;
kobject_init_type_name(&ddbond_info->item, name, &ddbond_info_type);
return &ddbond_info->item;
}
static ssize_t ddbond_description(struct kobject *item,
struct attribute *attr, char *page)
{
return sprintf(page,
"A ddbond block server has two components: a userspace server and a kernel\n"
"io daemon. First start the server and give it the name of a socket it will\n"
"create, then create a ddbond directory and write the same name into the\n"
"socket attribute\n");
}
static struct kobj_type ddbond_type = {
.sysfs_ops = &(struct sysfs_ops){
.show = ddbond_description,
},
.ct_group_ops = &(struct configfs_group_operations){
.make_item = ddbond_make_item,
},
.default_attrs = (struct attribute *[]){
&(struct attribute){
.owner = THIS_MODULE,
.name = "description",
.mode = S_IRUGO,
},
NULL,
}
};
static struct subsystem ddbond_subsys = {
.kset = {
.kobj = {
.k_name = "ddbond",
.ktype = &ddbond_type,
},
},
};
static int __init init_ddbond_config(void)
{
int ret;
config_group_init(&ddbond_subsys.kset);
init_rwsem(&ddbond_subsys.rwsem);
if ((ret = configfs_register_subsystem(&ddbond_subsys)))
printk(KERN_ERR "Error %d while registering subsystem %s\n",
ret, ddbond_subsys.kset.kobj.k_name);
return ret;
}
static void __exit exit_ddbond_config(void)
{
configfs_unregister_subsystem(&ddbond_subsys);
}
module_init(init_ddbond_config);
module_exit(exit_ddbond_config);
MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-08-30 23:28 ` Andrew Morton
@ 2005-08-30 23:34 ` viro
2005-08-30 23:51 ` Daniel Phillips
2005-08-30 23:37 ` Daniel Phillips
` (2 subsequent siblings)
3 siblings, 1 reply; 22+ messages in thread
From: viro @ 2005-08-30 23:34 UTC (permalink / raw)
To: Andrew Morton; +Cc: Joel Becker, phillips, linux-kernel, greg
On Tue, Aug 30, 2005 at 04:28:46PM -0700, Andrew Morton wrote:
> Sure, but all that copying-and-pasting really sucks. I'm sure there's some
> way of providing the slightly different semantics from the same codebase?
Careful - you've almost reinvented the concept of library, which would
violate any number of patents...
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-08-30 23:25 ` Daniel Phillips
@ 2005-08-30 23:35 ` Daniel Phillips
0 siblings, 0 replies; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 23:35 UTC (permalink / raw)
To: Joel Becker; +Cc: linux-kernel, Andrew Morton, Greg KH
On Wednesday 31 August 2005 09:25, Daniel Phillips wrote:
> On Wednesday 31 August 2005 09:13, Joel Becker wrote:
> > On Wed, Aug 31, 2005 at 08:54:39AM +1000, Daniel Phillips wrote:
> > > But it would be stupid to forbid users from creating directories in
> > > sysfs or to forbid kernel modules from directly tweaking a configfs
> > > namespace. Why should the kernel not be able to add objects to a
> > > directory a user created? It should be up to the module author to
> > > decide these things.
> >
> > This is precisely why configfs is separate from sysfs. If both
> > user and kernel can create objects, the lifetime of the object and its
> > filesystem representation is very complex. Sysfs already has problems
> > with people getting this wrong. configfs does not.
>
> Could you please give a specific case?
More to the point: what makes you think that this apparent ruggedness will
diminish after being re-integrated with sysfs? If you wish, you can avoid
any dangers by not using sysfs's vfs bypass api. It should be up to the
subsystem author.
Regards,
Daniel
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-08-30 23:28 ` Andrew Morton
2005-08-30 23:34 ` viro
@ 2005-08-30 23:37 ` Daniel Phillips
2005-08-31 0:03 ` Joel Becker
2005-09-04 3:53 ` Joel Becker
3 siblings, 0 replies; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 23:37 UTC (permalink / raw)
To: Andrew Morton; +Cc: Joel Becker, linux-kernel, greg
On Wednesday 31 August 2005 09:28, Andrew Morton wrote:
> Joel Becker <Joel.Becker@oracle.com> wrote:
> > On Wed, Aug 31, 2005 at 08:54:39AM +1000, Daniel Phillips wrote:
> > > But it would be stupid to forbid users from creating directories in
> > > sysfs or to forbid kernel modules from directly tweaking a configfs
> > > namespace. Why should the kernel not be able to add objects to a
> > > directory a user created? It should be up to the module author to
> > > decide these things.
> >
> > This is precisely why configfs is separate from sysfs. If both
> > user and kernel can create objects, the lifetime of the object and its
> > filesystem representation is very complex. Sysfs already has problems
> > with people getting this wrong. configfs does not.
> > The fact that sysfs and configfs have similar backing stores
> > does not make them the same thing.
>
> Sure, but all that copying-and-pasting really sucks. I'm sure there's some
> way of providing the slightly different semantics from the same codebase?
I will have that patch ready later this week.
Regards,
Daniel
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-08-30 23:34 ` viro
@ 2005-08-30 23:51 ` Daniel Phillips
0 siblings, 0 replies; 22+ messages in thread
From: Daniel Phillips @ 2005-08-30 23:51 UTC (permalink / raw)
To: viro; +Cc: Andrew Morton, Joel Becker, linux-kernel, greg
On Wednesday 31 August 2005 09:34, viro@ZenIV.linux.org.uk wrote:
> On Tue, Aug 30, 2005 at 04:28:46PM -0700, Andrew Morton wrote:
> > Sure, but all that copying-and-pasting really sucks. I'm sure there's
> > some way of providing the slightly different semantics from the same
> > codebase?
>
> Careful - you've almost reinvented the concept of library, which would
> violate any number of patents...
I will keep my eyes open for library candidates as I go. For example, the
binary blob operations really cry out for it.
Regards,
Daniel
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-08-30 23:28 ` Andrew Morton
2005-08-30 23:34 ` viro
2005-08-30 23:37 ` Daniel Phillips
@ 2005-08-31 0:03 ` Joel Becker
2005-09-04 3:53 ` Joel Becker
3 siblings, 0 replies; 22+ messages in thread
From: Joel Becker @ 2005-08-31 0:03 UTC (permalink / raw)
To: Andrew Morton; +Cc: phillips, linux-kernel, greg
[-- Attachment #1: Type: text/plain, Size: 929 bytes --]
On Tue, Aug 30, 2005 at 04:28:46PM -0700, Andrew Morton wrote:
> Joel Becker <Joel.Becker@oracle.com> wrote:
> > The fact that sysfs and configfs have similar backing stores
> > does not make them the same thing.
> >
>
> Sure, but all that copying-and-pasting really sucks. I'm sure there's some
> way of providing the slightly different semantics from the same codebase?
The way that configfs and sysfs create/destroy dentries and
their associated inodes is very different from the top, yet similar from
the bottom. I suspect that some of it could be libraryized. When I
first looked started configfs, I was starting from an "add on to sysfs"
perspective, after all. The sysfs maintainers and I agreed, after much
discussion, that we should go to a separate tree.
Joel
--
"Here's a nickle -- get yourself a better X server."
- Keith Packard
http://www.jlbec.org/
jlbec@evilplan.org
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-08-30 23:28 ` Andrew Morton
` (2 preceding siblings ...)
2005-08-31 0:03 ` Joel Becker
@ 2005-09-04 3:53 ` Joel Becker
2005-09-04 4:12 ` Joel Becker
3 siblings, 1 reply; 22+ messages in thread
From: Joel Becker @ 2005-09-04 3:53 UTC (permalink / raw)
To: Andrew Morton; +Cc: phillips, linux-kernel, greg
On Tue, Aug 30, 2005 at 04:28:46PM -0700, Andrew Morton wrote:
> Sure, but all that copying-and-pasting really sucks. I'm sure there's some
> way of providing the slightly different semantics from the same codebase?
First, let's look at sharing the primary structures.
[kobject vs config_item]
When I first started configfs, I tried to make it part of sysfs.
That was obviously a bad idea before I got very far at all (more on this
as we go along). So I forked the codebase. I wanted, however, to
preserve kobject and kset, because I figured we didn't need another
kernel object type. For the longest time usysfs (user-sysfs, as it was
originally called) used kobjects and jumped through hoops to make it work.
The base structures, kobject and config_item, are indeed
identical in physical layout. But in use, they are very different. A
kobject, when created, is expected to appear sysfs. You mistakenly
kobject_add(), whoops, there it is. kobject_init() tries to link a
kobject to the kobject->kset pointer, because it's expecting the kernel
to be handling the relationship.
In configfs, the relationship is handled by the VFS. The
linkage is purposefully controlled by the ->mkdir() codepath. The
client driver using this relationship can follow it, but never modify it
or call anything that modifies it.
In the earlier configfs incarnations that used kobject/kset, I
found myself having to fool the kobject infrastructure to get
kobject->kset correct. In addition, it was a complexity I had to always
think about. In configfs with config_item, it's not an issue because it
doesn't exist.
[kobj_type vs config_item_type]
kobj_type has three pointers. A release function for the
object, a set of sysfs_ops for attribute functions, and a set of
default_attrs.
config_item_type needs to know the module owner to pin the
client module. It has 5 item operations (this includes the parallels
for sysfs_ops) and 4 group operations (this is for config_group, the
parallel of kset). Because operation structures are often shared, these
are made into structures.
kobj_type would have to know all the features of
config_item_type, even though it is useless stuff for sysfs. Waste of
memory and code.
[subsystem vs configfs_subsystem]
The sysfs folks are trying to lose the semaphore from a sysfs
subsystem, because it makes sense to have locking that is finer grained.
It should apply to the granularity of the specific device or so.
configfs_subsystem needs the semaphore, because it protects only
one thing: the config_group/config_item hierarchy. When a client module
wants to navigate the tree of its groups and items, it merely takes the
semaphore and navigates. Once it finds and grabs a kref to what it
needs, it can drop it. There is no need for any more complex locking.
[kset vs config_group]
Here is where it gets really different. A kset may or may not
represent a directory exactly. kobjects can be part of a kset they
aren't under in the sysfs tree. configfs_items must be part of the
parent config_group, becuase the VFS tree (the dcache) is the exact
arbiter of the relationship.
Then there is the physical structure. A kset contains a pointer
to a kobj_type that its children will be assigned. This field is
useless in configfs, and more importantly, it could result in an
assignemnt that isn't related to the real object type, becuase of kset's
ability to have non-children members. This adds more complexity to the
code, as you have to handle, prevent, or kludge around this case.
kset contains a lock around its list of children. configfs
doesn't need this because the subsystem lock is protecting the hierarchy
here.
kset contains a hotplug ops pointer. configfs has nothing to do
with hotplug. So here are three pointers any configfs object has to
carry around for no reason other than sharing a structure.
Conversely, a config_group contains a list of default subgroups.
They are not attributes, they are groups. This is not the same as
config_item's default_attrs. Here, kset would carry around an extra
pointer for every object.
Given the stress everyone places on the memory usage of these
objects, adding useless pointers to both sides just to share the
structure seems a very bad idea.
--
"Well-timed silence hath more eloquence than speech."
- Martin Fraquhar Tupper
Joel Becker
Senior Member of Technical Staff
Oracle
E-mail: joel.becker@oracle.com
Phone: (650) 506-8127
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-09-04 3:53 ` Joel Becker
@ 2005-09-04 4:12 ` Joel Becker
2005-09-04 4:41 ` Joel Becker
0 siblings, 1 reply; 22+ messages in thread
From: Joel Becker @ 2005-09-04 4:12 UTC (permalink / raw)
To: Andrew Morton, phillips, linux-kernel, greg
On Tue, Aug 30, 2005 at 04:28:46PM -0700, Andrew Morton wrote:
> Sure, but all that copying-and-pasting really sucks. I'm sure there's some
> way of providing the slightly different semantics from the same codebase?
What about the backing store? Specifically, sysfs_dirent vs
configfs_dirent.
The structures are almost identical. What's different?
configfs has a list of symlinks, as these are hard linkages and involve
pinning and reference counting. So, to merge the structures, you have
to add two pointers (a list_head) to every sysfs object.
Allocating, initializing, and freeing them really does appear to
be virtually identical. The functions that call the creation are very
different, but they could call the same thing. There are more types of
things in configfs, so all shared calls would have to be able to handle
them.
Oh, but the get_name() functions, the one that return the string
name of a _dirent, are very different. So you'd have to add another
pointer to the structure, a ->get_name() callback. That's an additional
pointer for every sysfs object.
The attach_attr() functions are different. Some of that is the
BIN_ATTR type of sysfs, which configfs doesn't and shouldn't have. In
that case, the code still works, as BIN_ATTR test wouldn't succeed.
They configure dentry_ops, which are different in sysfs and configfs.
So the API would have to change to specify the appropriate dentry_ops.
This is certainly not insurmountable. I don't know what you'd
call it, fs/libfs/backing_store.c? I'm interested in what the sysfs
folks have to say on this, and how much they'd like to help.
Joel
--
A good programming language should have features that make the
kind of people who use the phrase "software engineering" shake
their heads disapprovingly.
- Paul Graham
Joel Becker
Senior Member of Technical Staff
Oracle
E-mail: joel.becker@oracle.com
Phone: (650) 506-8127
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-09-04 4:12 ` Joel Becker
@ 2005-09-04 4:41 ` Joel Becker
2005-09-04 4:54 ` Joel Becker
0 siblings, 1 reply; 22+ messages in thread
From: Joel Becker @ 2005-09-04 4:41 UTC (permalink / raw)
To: Andrew Morton, phillips, linux-kernel, greg
On Sat, Sep 03, 2005 at 09:12:24PM -0700, Joel Becker wrote:
> On Tue, Aug 30, 2005 at 04:28:46PM -0700, Andrew Morton wrote:
> > Sure, but all that copying-and-pasting really sucks. I'm sure there's some
> > way of providing the slightly different semantics from the same codebase?
The final piece of similar code is the buffered I/O setup for
attribute files. Here, the major difference is how config_items and
kobjects refer to their show/store operations. The functions have
different rules on this.
A kobject doesn't need show/store, it can provide one for the
entire kset or subsystem. A config_item necessarily has one for its
type, and cannot chain up. So somehow the code would need to know which
was the case.
A kobject directly has a sysfs_ops structure. The config_item
has show/store in a config_item_operations strucutre. If you split them
out, you add a pointer and some needless complexity. So the different
code paths need to refer to the functions differently.
The attribute and configfs_attribute structures are physically
identical, but the sysfs one has poor naming (IMHO). If struct
attribute wasn't going to change, and no magic would be added to its
usage (magic like kobject_add()'s intertwining with sysfs), they could
probably be shared.
Joel
--
"The doctrine of human equality reposes on this: that there is no
man really clever who has not found that he is stupid."
- Gilbert K. Chesterson
Joel Becker
Senior Member of Technical Staff
Oracle
E-mail: joel.becker@oracle.com
Phone: (650) 506-8127
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-09-04 4:41 ` Joel Becker
@ 2005-09-04 4:54 ` Joel Becker
2005-09-07 20:31 ` Greg KH
0 siblings, 1 reply; 22+ messages in thread
From: Joel Becker @ 2005-09-04 4:54 UTC (permalink / raw)
To: Andrew Morton, phillips, linux-kernel, greg
On Sat, Sep 03, 2005 at 09:41:36PM -0700, Joel Becker wrote:
> On Sat, Sep 03, 2005 at 09:12:24PM -0700, Joel Becker wrote:
> > On Tue, Aug 30, 2005 at 04:28:46PM -0700, Andrew Morton wrote:
> > > Sure, but all that copying-and-pasting really sucks. I'm sure there's some
> > > way of providing the slightly different semantics from the same codebase?
>
So, what conclusions can we draw from looking at this again?
First, merging the kobject/kset structures with the
config_item/config_group structures has one benefit: they have the same
name. However, that's also the first problem. As it currently stands,
you know from the name what you are working with. If they have the same
name, you don't know if you are working with an object in sysfs or
configfs. We call a pid "pid_t" rather than "unsigned int" for the same
reason, clarity of usage.
Merging them has a lot of other problems. Far from removing
code bloat (a configfs object and a sysfs object need different
initialization, etc, so there would be two paths anyway), it adds
significant memory bloat, as configfs objects and sysfs objects are
carrying around members for the other filesystem. Finally, there are
surprises in store if you try to use some of the API that isn't
appropriate. With a different name and type, you just _can't_ do that.
We could perhaps share the attribute structure. This has the
same name confusion problem above, but that's about it, assuming that
sysfs never wants to do anything smarter with them. I'm hoping the
sysfs folks comment on this.
Sharing the attribute file code is much harder. They operate on
different types, and those types are laid out differently. I think we'd
have to make the API far more invasive. Instead of black-boxing the API
and having only show/store to implement, we'd have to provide some sort
of translation from the native structure to a shared type. Basically,
repeating what the VFS has already done for us.
Finally, assuming that the sysfs_dirent/configfs_dirent
arrangement is pretty fleshed out, I think that perhaps this backing
store could be joined. Again, no more magic could be added, and it
would have to handle the sysfs and configfs types in concurrence, but I
think it could be done. Again, sysfs folks, please comment.
Joel
--
"Hell is oneself, hell is alone, the other figures in it, merely projections."
- T. S. Eliot
Joel Becker
Senior Member of Technical Staff
Oracle
E-mail: joel.becker@oracle.com
Phone: (650) 506-8127
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 1 of 4] Configfs is really sysfs
2005-09-04 4:54 ` Joel Becker
@ 2005-09-07 20:31 ` Greg KH
0 siblings, 0 replies; 22+ messages in thread
From: Greg KH @ 2005-09-07 20:31 UTC (permalink / raw)
To: Andrew Morton, phillips, linux-kernel
On Sat, Sep 03, 2005 at 09:54:45PM -0700, Joel Becker wrote:
> Finally, assuming that the sysfs_dirent/configfs_dirent
> arrangement is pretty fleshed out, I think that perhaps this backing
> store could be joined. Again, no more magic could be added, and it
> would have to handle the sysfs and configfs types in concurrence, but I
> think it could be done. Again, sysfs folks, please comment.
Thanks for the good explainations. I still agree with Joel that these
need to be separate fses.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2005-09-08 1:11 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-08-30 22:54 [RFC][PATCH 1 of 4] Configfs is really sysfs Daniel Phillips
2005-08-30 22:57 ` [RFC][PATCH 2 " Daniel Phillips
2005-08-30 22:59 ` [RFC][PATCH 3 " Daniel Phillips
2005-08-30 23:03 ` [RFC][PATCH 4 " Daniel Phillips
2005-08-30 23:30 ` Daniel Phillips
2005-08-30 23:06 ` [RFC][PATCH 3 " Stephen Hemminger
2005-08-30 23:18 ` Daniel Phillips
2005-08-30 23:10 ` Daniel Phillips
2005-08-30 23:22 ` [RFC][PATCH 2 " Daniel Phillips
2005-08-30 23:13 ` [RFC][PATCH 1 " Joel Becker
2005-08-30 23:25 ` Daniel Phillips
2005-08-30 23:35 ` Daniel Phillips
2005-08-30 23:28 ` Andrew Morton
2005-08-30 23:34 ` viro
2005-08-30 23:51 ` Daniel Phillips
2005-08-30 23:37 ` Daniel Phillips
2005-08-31 0:03 ` Joel Becker
2005-09-04 3:53 ` Joel Becker
2005-09-04 4:12 ` Joel Becker
2005-09-04 4:41 ` Joel Becker
2005-09-04 4:54 ` Joel Becker
2005-09-07 20:31 ` Greg KH
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox