Commit 7bf68565 authored by Patrick Mochel's avatar Patrick Mochel

[sysfs] Add attribute groups

Attribute groups provide the ability to register a set of sysfs attributes
for a kobject all at once, and optionally create a subdirectory to hold 
them.

The constructs are simple:
                                                                                                         
struct attribute_group {
        char                    * name;
        struct attribute        * attrs;
};

int sysfs_create_group(struct kobject *, struct attribute_group *);
void sysfs_remove_group(struct kobject *, struct attribute_group *);


If ->name is not NULL, then we create a subdirectory of that name to hold
the attributes. We then iterate over ->attrs and create a file for each,
in the subdirectory if we created one.

This prevents one from having to register a kobject (and define a new
kobj_type) to create a subdirectory for a group of attributes. Attributes
currently defined in that way can be converted to use attribute_groups
easily, with one caveat:

The attributes that are added for a kobject, even if in a subdirectory,
must be declared as the high-level attribute type (with an embedded struct
attribute) for the kobject, and conform to the kobj_type's calling
convention for reading/writing attributes.

The kobject that you're creating attributes for owns the directory, and
will assume ownership of the subdirectory. sysfs will reference this
kobject, and it's kobj_type, when the attribute file is opened to
determine the methods for reading/writing the attribute.

sysfs will call the kobj_type's show()/store() methods, which will convert
the kobject into a high-level object type, and convert the attribute into
a high-level attribute type, which (depending on the kobj_type) is
expected to have a show() and/or store() method.

Note that this makes it only slightly easier to create attributes en masse,
though it is a bit technically superior, since it doesn't require a new 
kobj_type and kobject register. More will come in this area.. 
parent defec5af
......@@ -2,4 +2,5 @@
# Makefile for the sysfs virtual filesystem
#
obj-y := inode.o file.o dir.o symlink.o mount.o bin.o
obj-y := inode.o file.o dir.o symlink.o mount.o bin.o \
group.o
......@@ -20,6 +20,38 @@ static int init_dir(struct inode * inode)
return 0;
}
static struct dentry *
create_dir(struct kobject * k, struct dentry * p, char * n)
{
struct dentry * dentry;
down(&p->d_inode->i_sem);
dentry = sysfs_get_dentry(p,n);
if (!IS_ERR(dentry)) {
int error;
dentry->d_fsdata = (void *)k;
error = sysfs_create(dentry,
(S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO),
init_dir);
if (!error)
p->d_inode->i_nlink++;
else {
dput(dentry);
dentry = ERR_PTR(error);
}
}
up(&p->d_inode->i_sem);
return dentry;
}
struct dentry * sysfs_create_subdir(struct kobject * k, char * n)
{
return create_dir(k,k->dentry,n);
}
/**
* sysfs_create_dir - create a directory for an object.
* @parent: parent parent object.
......@@ -42,23 +74,34 @@ int sysfs_create_dir(struct kobject * kobj)
else
return -EFAULT;
down(&parent->d_inode->i_sem);
dentry = sysfs_get_dentry(parent,kobj->name);
if (!IS_ERR(dentry)) {
dentry->d_fsdata = (void *)kobj;
dentry = create_dir(kobj,parent,kobj->name);
if (!IS_ERR(dentry))
kobj->dentry = dentry;
error = sysfs_create(dentry,(S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO),
init_dir);
if (!error)
parent->d_inode->i_nlink++;
} else
else
error = PTR_ERR(dentry);
up(&parent->d_inode->i_sem);
return error;
}
static void remove_dir(struct dentry * d)
{
struct dentry * parent = dget(d->d_parent);
down(&parent->d_inode->i_sem);
d_delete(d);
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 an object's directory.
......@@ -73,14 +116,11 @@ void sysfs_remove_dir(struct kobject * kobj)
{
struct list_head * node;
struct dentry * dentry = dget(kobj->dentry);
struct dentry * parent;
if (!dentry)
return;
pr_debug("sysfs %s: removing dir\n",dentry->d_name.name);
parent = dget(dentry->d_parent);
down(&parent->d_inode->i_sem);
down(&dentry->d_inode->i_sem);
spin_lock(&dcache_lock);
......@@ -107,18 +147,12 @@ void sysfs_remove_dir(struct kobject * kobj)
}
spin_unlock(&dcache_lock);
up(&dentry->d_inode->i_sem);
d_delete(dentry);
simple_rmdir(parent->d_inode,dentry);
pr_debug(" o %s removing done (%d)\n",dentry->d_name.name,
atomic_read(&dentry->d_count));
remove_dir(dentry);
/**
* Drop reference from dget() on entrance.
*/
dput(dentry);
up(&parent->d_inode->i_sem);
dput(parent);
}
void sysfs_rename_dir(struct kobject * kobj, char *new_name)
......
......@@ -344,35 +344,38 @@ static struct file_operations sysfs_file_operations = {
.release = sysfs_release,
};
/**
* sysfs_create_file - create an attribute file for an object.
* @kobj: object we're creating for.
* @attr: atrribute descriptor.
*/
int sysfs_create_file(struct kobject * kobj, struct attribute * attr)
int sysfs_add_file(struct dentry * dir, struct attribute * attr)
{
struct dentry * dentry;
struct dentry * parent;
int error = 0;
if (!kobj || !attr)
return -EINVAL;
parent = kobj->dentry;
int error;
down(&parent->d_inode->i_sem);
dentry = sysfs_get_dentry(parent,attr->name);
down(&dir->d_inode->i_sem);
dentry = sysfs_get_dentry(dir,attr->name);
if (!IS_ERR(dentry)) {
dentry->d_fsdata = (void *)attr;
error = sysfs_create(dentry,(attr->mode & S_IALLUGO) | S_IFREG,init_file);
} else
error = PTR_ERR(dentry);
up(&parent->d_inode->i_sem);
up(&dir->d_inode->i_sem);
return error;
}
/**
* sysfs_create_file - create an attribute file for an object.
* @kobj: object we're creating for.
* @attr: atrribute descriptor.
*/
int sysfs_create_file(struct kobject * kobj, struct attribute * attr)
{
if (kobj && attr)
return sysfs_add_file(kobj->dentry,attr);
return -EINVAL;
}
/**
* sysfs_update_file - update the modified timestamp on an object attribute.
* @kobj: object we're acting for.
......
/*
* 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.
*
*/
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/dcache.h>
#include <linux/err.h>
#include "sysfs.h"
static void remove_files(struct dentry * dir, struct attribute_group * grp)
{
struct attribute * attr;
for (attr = grp->attrs; attr->name; attr++)
sysfs_hash_and_remove(dir,attr->name);
}
static int create_files(struct kobject * kobj, struct dentry * dir,
struct attribute_group * grp)
{
struct attribute * attr;
int error = 0;
for (attr = grp->attrs; attr->name && !error; attr++) {
error = sysfs_add_file(dir,attr);
}
if (error)
remove_files(dir,grp);
return error;
}
int sysfs_create_group(struct kobject * kobj, struct attribute_group * grp)
{
struct dentry * dir;
int error;
if (grp->name) {
dir = sysfs_create_subdir(kobj,grp->name);
if (IS_ERR(dir))
return PTR_ERR(dir);
} else
dir = kobj->dentry;
dir = dget(dir);
if ((error = create_files(kobj,dir,grp))) {
if (grp->name)
sysfs_remove_subdir(dir);
dput(dir);
}
return error;
}
void sysfs_remove_group(struct kobject * kobj, struct attribute_group * grp)
{
struct dentry * dir;
if (grp->name)
dir = sysfs_get_dentry(kobj->dentry,grp->name);
else
dir = kobj->dentry;
remove_files(dir,grp);
dput(dir);
if (grp->name)
sysfs_remove_subdir(dir);
}
EXPORT_SYMBOL(sysfs_create_group);
EXPORT_SYMBOL(sysfs_remove_group);
......@@ -6,5 +6,8 @@ extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *));
extern struct dentry * sysfs_get_dentry(struct dentry *, char *);
extern int sysfs_add_file(struct dentry * dir, struct attribute * attr);
extern void sysfs_hash_and_remove(struct dentry * dir, const char * name);
extern struct dentry * sysfs_create_subdir(struct kobject *, char *);
extern void sysfs_remove_subdir(struct dentry *);
......@@ -57,4 +57,13 @@ sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name);
extern void
sysfs_remove_link(struct kobject *, char * name);
struct attribute_group {
char * name;
struct attribute * attrs;
};
int sysfs_create_group(struct kobject *, struct attribute_group *);
void sysfs_remove_group(struct kobject *, struct attribute_group *);
#endif /* _SYSFS_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment