Commit ead7001e authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://ldm.bkbits.net/linux-2.5-kobject

into penguin.transmeta.com:/home/penguin/torvalds/repositories/kernel/linux
parents 6b668e88 b835de74
kobjects - Simple, Generic Kernel Objects
Patrick Mochel <mochel@osdl.org>
30 October 2002
kobjects
struct kobject introduces a simple, intregral datatype and a simple
set of semantics for operating on the device. kobjects are intended to
be embedded in larger data structures and replace fields it
duplicates. A set of library functions has been developed to assist in
the manipulation of kobjects.
struct kobject looks like this:
struct kobject {
char name[16];
atomic_t refcount;
struct list_head entry;
struct kobject * parent;
struct subsystem * subsys;
struct dentry * dentry;
};
void kobject_init(struct kobject *);
int kobject_register(struct kobject *);
void kobject_unregister(struct kobject *);
struct kobject * kobject_get(struct kobject *);
void kobject_put(struct kobject *);
subsystems
struct subsystem is introduced to describe a collection of objects of
a certain type. subsystems are kobjects themselves, though they
contain lists of kobjects that belong to that subsystem. Objects of a
subsystem (the embedder objects in which kobjects live) are all of the
same type. The interface looks like:
struct subsystem {
struct kobject kobj;
struct list_head list;
struct rw_semaphore rwsem;
struct subsystem * parent;
void (*release)(struct kobject *);
struct sysfs_ops * sysfs_ops;
struct attribute ** default_attrs;
};
void subsystem_init(struct subsystem *);
int subsystem_register(struct subsystem *);
void subsystem_unregister(struct subsystem *);
struct subsystem * subsys_get(struct subsystem * s);
void subsys_put(struct subsystem * s);
Familial Relations
kobjects and subsystems intersect and intertwine in several ways. Each
is well-defined (though maybe they could be made simpler). Each kobject
belongs to a subsystem. Since subsystems are kobjects themselves, they
also belong to a controlling subsystem. This implies that subsystems
are hierarchial.
Many kobjects are hierarchial in nature, which is represented by
including a pointer to its parent kobject in struct kobject. Many
different types of kobject-embedding objects may all point to the same
parent.
The ancestral hierarchy of kobjects should not be confused with
membership in a subsystem, or the ancestral relationship of
subsystems. A set of kobjects may all belong to a subsystem, but all
have different parents.
kobjects may be orphans and have no explicit parent. In that case, the
subsystem to which the object belongs becomes its parent.
Sysfs
These rules force a complete kobject hierarchy, which Suprise! maps
very well onto a filesystem.
driverfs was recently cloned, and there now exists sysfs. All driverfs
operations operate on a separate data type: struct driver_dir_entry,
which all objects that are represented in driverfs must have. driverfs
also allowed rogue directory creation that had no explicit objects
associated with them.
struct kobject is intended to be the common data type which sysfs
operates on. This gives the filesystem the ability to directly access
more fields of the object, including the reference count. This also
forces each directory in the filesystem to be tied directly to a
kobject.
Directory Placement
Parental relationships are determined in the kobject/subsystem layer,
and the kobject is then passed off to the sysfs layer. kobjects with
no parent have directories created for them in the sysfs root
directory. Per the rules above, the only kobjects that remain orphans
are subsystems without parent subsystems (since leaf objects either
have an explicit parent, or are assigned their controlling subsystem
as their foster parent).
File Callbacks
Previously, each driverfs directory contained a pointer to a list of file
operations for reading and writing driverfs files. These callbacks
received a struct driver_dir_entry, when they performed a
container_of() transform on to receive the specific object type for
which the call was meant.
These callbacks have been converted to accept a struct kobject instead
of struct driver_dir_entry. Since all kobjects belong to a subsystem
that contains kobjects all of the same type, the sysfs operations
have been moved to reside in the subsystem, since they are common for
all kobjects.
Default Attributes
Most subsystems have a set of default attributes associated with an
object that registers with them. A subsystem definition may contain a
NULL-terminated array of attributes that will be exported when an
object is registered with the subsystem.
Reference Counting
All objects contain reference counts. All functions accessing objects
should increment the reference count until they are finished, and
decrement the reference count. When an object is initialized, it
receives a reference count of 1. When a device is unregistered, the
reference is decremented. When the reference counts reaches 0, the
subsystem's ->release() callback for that object type (remember
subsystems control only one type of device each) is called; and the
reference counts of the kobject's subsystem and parent are
decremented.
The ->release() callback is the opportunity for the subsystem to free
memory allocated for the object. It is the notification that
absolutely no one is using the structure any more (and can't acquire a
reference to it), so it is safe to free it.
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/sysfs.h> #include <linux/kobject.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
static struct super_operations sysfs_ops; static struct super_operations sysfs_ops;
static struct file_operations sysfs_file_operations; static struct file_operations sysfs_file_operations;
static struct inode_operations sysfs_dir_inode_operations;
static struct address_space_operations sysfs_aops; static struct address_space_operations sysfs_aops;
static struct vfsmount *sysfs_mount; static struct vfsmount *sysfs_mount;
...@@ -52,45 +51,6 @@ static struct backing_dev_info sysfs_backing_dev_info = { ...@@ -52,45 +51,6 @@ static struct backing_dev_info sysfs_backing_dev_info = {
.memory_backed = 1, /* Does not contribute to dirty memory */ .memory_backed = 1, /* Does not contribute to dirty memory */
}; };
static int sysfs_readpage(struct file *file, struct page * page)
{
if (!PageUptodate(page)) {
void *kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr, 0, PAGE_CACHE_SIZE);
flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0);
SetPageUptodate(page);
}
unlock_page(page);
return 0;
}
static int sysfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
{
if (!PageUptodate(page)) {
void *kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr, 0, PAGE_CACHE_SIZE);
flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0);
SetPageUptodate(page);
}
return 0;
}
static int sysfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
{
struct inode *inode = page->mapping->host;
loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
set_page_dirty(page);
if (pos > inode->i_size)
inode->i_size = pos;
return 0;
}
static struct inode *sysfs_get_inode(struct super_block *sb, int mode, int dev) static struct inode *sysfs_get_inode(struct super_block *sb, int mode, int dev)
{ {
struct inode *inode = new_inode(sb); struct inode *inode = new_inode(sb);
...@@ -113,7 +73,7 @@ static struct inode *sysfs_get_inode(struct super_block *sb, int mode, int dev) ...@@ -113,7 +73,7 @@ static struct inode *sysfs_get_inode(struct super_block *sb, int mode, int dev)
inode->i_fop = &sysfs_file_operations; inode->i_fop = &sysfs_file_operations;
break; break;
case S_IFDIR: case S_IFDIR:
inode->i_op = &sysfs_dir_inode_operations; inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations; inode->i_fop = &simple_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */ /* directory inodes start off with i_nlink == 2 (for "." entry) */
...@@ -182,29 +142,6 @@ static int sysfs_symlink(struct inode * dir, struct dentry *dentry, const char * ...@@ -182,29 +142,6 @@ static int sysfs_symlink(struct inode * dir, struct dentry *dentry, const char *
return error; return error;
} }
static inline int sysfs_positive(struct dentry *dentry)
{
return (dentry->d_inode && !d_unhashed(dentry));
}
static int sysfs_empty(struct dentry *dentry)
{
struct list_head *list;
spin_lock(&dcache_lock);
list_for_each(list, &dentry->d_subdirs) {
struct dentry *de = list_entry(list, struct dentry, d_child);
if (sysfs_positive(de)) {
spin_unlock(&dcache_lock);
return 0;
}
}
spin_unlock(&dcache_lock);
return 1;
}
static int sysfs_unlink(struct inode *dir, struct dentry *dentry) static int sysfs_unlink(struct inode *dir, struct dentry *dentry)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
...@@ -217,30 +154,34 @@ static int sysfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -217,30 +154,34 @@ static int sysfs_unlink(struct inode *dir, struct dentry *dentry)
} }
/** /**
* sysfs_read_file - "read" data from a file. * sysfs_read_file - read an attribute.
* @file: file pointer * @file: file pointer.
* @buf: buffer to fill * @buf: buffer to fill.
* @count: number of bytes to read * @count: number of bytes to read.
* @ppos: starting offset in file * @ppos: starting offset in file.
*
* 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.
* *
* Userspace wants data from a file. It is up to the creator of the file to * We allocate a %PAGE_SIZE buffer, and pass it to the object's ->show()
* provide that data. * method (along with the object). We loop doing this until @count is
* There is a struct device_attribute embedded in file->private_data. We * satisfied, or ->show() returns %0.
* obtain that and check if the read callback is implemented. If so, we call
* it, passing the data field of the file entry.
* Said callback is responsible for filling the buffer and returning the number
* of bytes it put in it. We update @ppos correctly.
*/ */
static ssize_t static ssize_t
sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos) sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos)
{ {
struct attribute * attr = file->f_dentry->d_fsdata; struct attribute * attr = file->f_dentry->d_fsdata;
struct driver_dir_entry * dir; struct sysfs_ops * ops = NULL;
struct kobject * kobj;
unsigned char *page; unsigned char *page;
ssize_t retval = 0; ssize_t retval = 0;
dir = file->f_dentry->d_parent->d_fsdata; kobj = file->f_dentry->d_parent->d_fsdata;
if (!dir->ops->show) if (kobj && kobj->subsys)
ops = kobj->subsys->sysfs_ops;
if (!ops || !ops->show)
return 0; return 0;
if (count > PAGE_SIZE) if (count > PAGE_SIZE)
...@@ -253,7 +194,7 @@ sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos) ...@@ -253,7 +194,7 @@ sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos)
while (count > 0) { while (count > 0) {
ssize_t len; ssize_t len;
len = dir->ops->show(dir,attr,page,count,*ppos); len = ops->show(kobj,attr,page,count,*ppos);
if (len <= 0) { if (len <= 0) {
if (len < 0) if (len < 0)
...@@ -277,27 +218,32 @@ sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos) ...@@ -277,27 +218,32 @@ sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos)
} }
/** /**
* sysfs_write_file - "write" to a file * sysfs_write_file - write an attribute.
* @file: file pointer * @file: file pointer
* @buf: data to write * @buf: data to write
* @count: number of bytes * @count: number of bytes
* @ppos: starting offset * @ppos: starting offset
* *
* Similarly to sysfs_read_file, we act essentially as a bit pipe. * Identical to sysfs_read_file(), though going the opposite direction.
* We check for a "write" callback in file->private_data, and pass * We allocate a %PAGE_SIZE buffer and copy in the userspace buffer. We
* @buffer, @count, @ppos, and the file entry's data to the callback. * pass that to the object's ->store() method until we reach @count or
* The number of bytes written is returned, and we handle updating * ->store() returns %0 or less.
* @ppos properly.
*/ */
static ssize_t static ssize_t
sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos) sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos)
{ {
struct attribute * attr = file->f_dentry->d_fsdata; struct attribute * attr = file->f_dentry->d_fsdata;
struct driver_dir_entry * dir; struct sysfs_ops * ops = NULL;
struct kobject * kobj;
ssize_t retval = 0; ssize_t retval = 0;
char * page; char * page;
dir = file->f_dentry->d_parent->d_fsdata; kobj = file->f_dentry->d_parent->d_fsdata;
if (kobj && kobj->subsys)
ops = kobj->subsys->sysfs_ops;
if (!ops || !ops->store)
return 0;
page = (char *)__get_free_page(GFP_KERNEL); page = (char *)__get_free_page(GFP_KERNEL);
if (!page) if (!page)
...@@ -312,7 +258,7 @@ sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos) ...@@ -312,7 +258,7 @@ sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos)
while (count > 0) { while (count > 0) {
ssize_t len; ssize_t len;
len = dir->ops->store(dir,attr,page + retval,count,*ppos); len = ops->store(kobj,attr,page + retval,count,*ppos);
if (len <= 0) { if (len <= 0) {
if (len < 0) if (len < 0)
...@@ -329,77 +275,42 @@ sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos) ...@@ -329,77 +275,42 @@ sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos)
return retval; return retval;
} }
static loff_t
sysfs_file_lseek(struct file *file, loff_t offset, int orig)
{
loff_t retval = -EINVAL;
down(&file->f_dentry->d_inode->i_sem);
switch(orig) {
case 0:
if (offset > 0) {
file->f_pos = offset;
retval = file->f_pos;
}
break;
case 1:
if ((offset + file->f_pos) > 0) {
file->f_pos += offset;
retval = file->f_pos;
}
break;
default:
break;
}
up(&file->f_dentry->d_inode->i_sem);
return retval;
}
static int sysfs_open_file(struct inode * inode, struct file * filp) static int sysfs_open_file(struct inode * inode, struct file * filp)
{ {
struct driver_dir_entry * dir; struct kobject * kobj;
int error = 0; int error = 0;
dir = (struct driver_dir_entry *)filp->f_dentry->d_parent->d_fsdata; kobj = filp->f_dentry->d_parent->d_fsdata;
if (dir) { if ((kobj = kobject_get(kobj))) {
struct attribute * attr = filp->f_dentry->d_fsdata; struct attribute * attr = filp->f_dentry->d_fsdata;
if (attr && dir->ops) { if (!attr)
if (dir->ops->open) error = -EINVAL;
error = dir->ops->open(dir); } else
goto Done;
}
}
error = -EINVAL; error = -EINVAL;
Done:
return error; return error;
} }
static int sysfs_release(struct inode * inode, struct file * filp) static int sysfs_release(struct inode * inode, struct file * filp)
{ {
struct driver_dir_entry * dir; struct kobject * kobj = filp->f_dentry->d_parent->d_fsdata;
dir = (struct driver_dir_entry *)filp->f_dentry->d_parent->d_fsdata; if (kobj)
if (dir->ops->close) kobject_put(kobj);
dir->ops->close(dir);
return 0; return 0;
} }
static struct file_operations sysfs_file_operations = { static struct file_operations sysfs_file_operations = {
.read = sysfs_read_file, .read = sysfs_read_file,
.write = sysfs_write_file, .write = sysfs_write_file,
.llseek = sysfs_file_lseek, .llseek = generic_file_llseek,
.open = sysfs_open_file, .open = sysfs_open_file,
.release = sysfs_release, .release = sysfs_release,
}; };
static struct inode_operations sysfs_dir_inode_operations = {
.lookup = simple_lookup,
};
static struct address_space_operations sysfs_aops = { static struct address_space_operations sysfs_aops = {
.readpage = sysfs_readpage, .readpage = simple_readpage,
.writepage = fail_writepage, .writepage = fail_writepage,
.prepare_write = sysfs_prepare_write, .prepare_write = simple_prepare_write,
.commit_write = sysfs_commit_write .commit_write = simple_commit_write
}; };
static struct super_operations sysfs_ops = { static struct super_operations sysfs_ops = {
...@@ -464,6 +375,7 @@ static int __init sysfs_init(void) ...@@ -464,6 +375,7 @@ static int __init sysfs_init(void)
core_initcall(sysfs_init); core_initcall(sysfs_init);
static struct dentry * get_dentry(struct dentry * parent, const char * name) static struct dentry * get_dentry(struct dentry * parent, const char * name)
{ {
struct qstr qstr; struct qstr qstr;
...@@ -474,137 +386,207 @@ static struct dentry * get_dentry(struct dentry * parent, const char * name) ...@@ -474,137 +386,207 @@ static struct dentry * get_dentry(struct dentry * parent, const char * name)
return lookup_hash(&qstr,parent); return lookup_hash(&qstr,parent);
} }
/** /**
* sysfs_create_dir - create a directory in the filesystem * sysfs_create_dir - create a directory for an object.
* @entry: directory entry * @parent: parent parent object.
* @parent: parent directory entry * @kobj: object we're creating directory for.
*/ */
int
sysfs_create_dir(struct driver_dir_entry * entry, int sysfs_create_dir(struct kobject * kobj)
struct driver_dir_entry * parent)
{ {
struct dentry * dentry = NULL; struct dentry * dentry = NULL;
struct dentry * parent_dentry; struct dentry * parent;
int error = 0; int error = 0;
if (!entry) if (!kobj)
return -EINVAL; return -EINVAL;
parent_dentry = parent ? parent->dentry : NULL; if (kobj->parent)
parent = kobj->parent->dentry;
if (!parent_dentry) else if (sysfs_mount && sysfs_mount->mnt_sb)
if (sysfs_mount && sysfs_mount->mnt_sb) parent = sysfs_mount->mnt_sb->s_root;
parent_dentry = sysfs_mount->mnt_sb->s_root; else
if (!parent_dentry)
return -EFAULT; return -EFAULT;
down(&parent_dentry->d_inode->i_sem); down(&parent->d_inode->i_sem);
dentry = get_dentry(parent_dentry,entry->name); dentry = get_dentry(parent,kobj->name);
if (!IS_ERR(dentry)) { if (!IS_ERR(dentry)) {
dentry->d_fsdata = (void *) entry; dentry->d_fsdata = (void *)kobj;
entry->dentry = dentry; kobj->dentry = dentry;
error = sysfs_mkdir(parent_dentry->d_inode,dentry,entry->mode); error = sysfs_mkdir(parent->d_inode,dentry,
(S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO));
} else } else
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
up(&parent_dentry->d_inode->i_sem); up(&parent->d_inode->i_sem);
return error; return error;
} }
/** /**
* sysfs_create_file - create a file * sysfs_create_file - create an attribute file for an object.
* @entry: structure describing the file * @kobj: object we're creating for.
* @parent: directory to create it in * @attr: atrribute descriptor.
*/ */
int
sysfs_create_file(struct attribute * entry, int sysfs_create_file(struct kobject * kobj, struct attribute * attr)
struct driver_dir_entry * parent)
{ {
struct dentry * dentry; struct dentry * dentry;
struct dentry * parent;
int error = 0; int error = 0;
if (!entry || !parent) if (!kobj || !attr)
return -EINVAL; return -EINVAL;
if (!parent->dentry) parent = kobj->dentry;
return -EINVAL;
down(&parent->dentry->d_inode->i_sem); down(&parent->d_inode->i_sem);
dentry = get_dentry(parent->dentry,entry->name); dentry = get_dentry(parent,attr->name);
if (!IS_ERR(dentry)) { if (!IS_ERR(dentry)) {
dentry->d_fsdata = (void *)entry; dentry->d_fsdata = (void *)attr;
error = sysfs_create(parent->dentry->d_inode,dentry,entry->mode); error = sysfs_create(parent->d_inode,dentry,attr->mode);
} else } else
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
up(&parent->dentry->d_inode->i_sem); up(&parent->d_inode->i_sem);
return error; return error;
} }
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(p->name) + 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(p->name);
/* back up enough to print this bus id with '/' */
length -= cur;
strncpy(buffer + length,p->name,cur);
*(buffer + --length) = '/';
}
}
/** /**
* sysfs_create_symlink - make a symlink * sysfs_create_link - create symlink between two objects.
* @parent: directory we're creating in * @kobj: object whose directory we're creating the link in.
* @entry: entry describing link * @target: object we're pointing to.
* @target: place we're symlinking to * @name: name of the symlink.
*
*/ */
int sysfs_create_symlink(struct driver_dir_entry * parent, int sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name)
char * name, char * target)
{ {
struct dentry * dentry; struct dentry * dentry = kobj->dentry;
struct dentry * d;
int error = 0; int error = 0;
int size;
int depth;
char * path;
char * s;
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);
path = kmalloc(size,GFP_KERNEL);
if (!path)
return -ENOMEM;
memset(path,0,size);
if (!parent || !parent->dentry) for (s = path; depth--; s += 3)
return -EINVAL; strcpy(s,"../");
down(&parent->dentry->d_inode->i_sem); fill_object_path(target,path,size);
dentry = get_dentry(parent->dentry,name); pr_debug("%s: path = '%s'\n",__FUNCTION__,path);
if (!IS_ERR(dentry))
error = sysfs_symlink(parent->dentry->d_inode,dentry,target); down(&dentry->d_inode->i_sem);
d = get_dentry(dentry,name);
if (!IS_ERR(d))
error = sysfs_symlink(dentry->d_inode,d,path);
else else
error = PTR_ERR(dentry); error = PTR_ERR(d);
up(&parent->dentry->d_inode->i_sem); up(&dentry->d_inode->i_sem);
kfree(path);
return error; return error;
} }
static void hash_and_remove(struct dentry * dir, const char * name)
{
struct dentry * victim;
down(&dir->d_inode->i_sem);
victim = get_dentry(dir,name);
if (!IS_ERR(victim)) {
/* make sure dentry is really there */
if (victim->d_inode &&
(victim->d_parent->d_inode == dir->d_inode)) {
sysfs_unlink(dir->d_inode,victim);
}
}
up(&dir->d_inode->i_sem);
}
/** /**
* sysfs_remove_file - exported file removal * sysfs_remove_file - remove an object attribute.
* @dir: directory the file supposedly resides in * @kobj: object we're acting for.
* @name: name of the file * @attr: attribute descriptor.
* *
* Try and find the file in the dir's list. * Hash the attribute name and kill the victim.
* If it's there, call __remove_file() (above) for the dentry.
*/ */
void sysfs_remove_file(struct driver_dir_entry * dir, const char * name)
void sysfs_remove_file(struct kobject * kobj, struct attribute * attr)
{ {
struct dentry * dentry; hash_and_remove(kobj->dentry,attr->name);
}
if (!dir->dentry)
return;
down(&dir->dentry->d_inode->i_sem); /**
dentry = get_dentry(dir->dentry,name); * sysfs_remove_link - remove symlink in object's directory.
if (!IS_ERR(dentry)) { * @kobj: object we're acting for.
/* make sure dentry is really there */ * @name: name of the symlink to remove.
if (dentry->d_inode && */
(dentry->d_parent->d_inode == dir->dentry->d_inode)) {
sysfs_unlink(dir->dentry->d_inode,dentry); void sysfs_remove_link(struct kobject * kobj, char * name)
} {
} hash_and_remove(kobj->dentry,name);
up(&dir->dentry->d_inode->i_sem);
} }
/** /**
* sysfs_remove_dir - exportable directory removal * sysfs_remove_dir - remove an object's directory.
* @dir: directory to remove * @kobj: object.
* *
* To make sure we don't orphan anyone, first remove * The only thing special about this is that we remove any files in
* all the children in the list, then do clean up the directory. * the directory before we remove the directory, and we've inlined
* what used to be sysfs_rmdir() below, instead of calling separately.
*/ */
void sysfs_remove_dir(struct driver_dir_entry * dir)
void sysfs_remove_dir(struct kobject * kobj)
{ {
struct list_head * node, * next; struct list_head * node, * next;
struct dentry * dentry = dir->dentry; struct dentry * dentry = kobj->dentry;
struct dentry * parent; struct dentry * parent;
if (!dentry) if (!dentry)
...@@ -622,7 +604,7 @@ void sysfs_remove_dir(struct driver_dir_entry * dir) ...@@ -622,7 +604,7 @@ void sysfs_remove_dir(struct driver_dir_entry * dir)
} }
d_invalidate(dentry); d_invalidate(dentry);
if (sysfs_empty(dentry)) { if (simple_empty(dentry)) {
dentry->d_inode->i_nlink -= 2; dentry->d_inode->i_nlink -= 2;
dentry->d_inode->i_flags |= S_DEAD; dentry->d_inode->i_flags |= S_DEAD;
parent->d_inode->i_nlink--; parent->d_inode->i_nlink--;
...@@ -635,8 +617,9 @@ void sysfs_remove_dir(struct driver_dir_entry * dir) ...@@ -635,8 +617,9 @@ void sysfs_remove_dir(struct driver_dir_entry * dir)
} }
EXPORT_SYMBOL(sysfs_create_file); EXPORT_SYMBOL(sysfs_create_file);
EXPORT_SYMBOL(sysfs_create_symlink);
EXPORT_SYMBOL(sysfs_create_dir);
EXPORT_SYMBOL(sysfs_remove_file); EXPORT_SYMBOL(sysfs_remove_file);
EXPORT_SYMBOL(sysfs_create_link);
EXPORT_SYMBOL(sysfs_remove_link);
EXPORT_SYMBOL(sysfs_create_dir);
EXPORT_SYMBOL(sysfs_remove_dir); EXPORT_SYMBOL(sysfs_remove_dir);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/*
* kobject.h - generic kernel object infrastructure.
*
*/
#ifndef _KOBJECT_H_
#define _KOBJECT_H_
#include <linux/types.h>
#include <linux/list.h>
#include <linux/sysfs.h>
#include <linux/rwsem.h>
#include <asm/atomic.h>
struct kobject {
char name[16];
atomic_t refcount;
struct list_head entry;
struct kobject * parent;
struct subsystem * subsys;
struct dentry * dentry;
};
extern void kobject_init(struct kobject *);
extern int kobject_register(struct kobject *);
extern void kobject_unregister(struct kobject *);
extern struct kobject * kobject_get(struct kobject *);
extern void kobject_put(struct kobject *);
struct subsystem {
struct kobject kobj;
struct list_head list;
struct rw_semaphore rwsem;
struct subsystem * parent;
void (*release)(struct kobject *);
struct sysfs_ops * sysfs_ops;
struct attribute ** default_attrs;
};
extern void subsystem_init(struct subsystem *);
extern int subsystem_register(struct subsystem *);
extern void subsystem_unregister(struct subsystem *);
static inline struct subsystem * subsys_get(struct subsystem * s)
{
return container_of(kobject_get(&s->kobj),struct subsystem,kobj);
}
static inline void subsys_put(struct subsystem * s)
{
kobject_put(&s->kobj);
}
#endif /* _KOBJECT_H_ */
...@@ -11,19 +11,11 @@ ...@@ -11,19 +11,11 @@
struct driver_dir_entry; struct driver_dir_entry;
struct attribute; struct attribute;
struct kobject;
struct sysfs_ops { struct sysfs_ops {
int (*open)(struct driver_dir_entry *); ssize_t (*show)(struct kobject *, struct attribute *,char *, size_t, loff_t);
int (*close)(struct driver_dir_entry *); ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t, loff_t);
ssize_t (*show)(struct driver_dir_entry *, struct attribute *,char *, size_t, loff_t);
ssize_t (*store)(struct driver_dir_entry *,struct attribute *,const char *, size_t, loff_t);
};
struct driver_dir_entry {
char * name;
struct dentry * dentry;
mode_t mode;
struct sysfs_ops * ops;
}; };
struct attribute { struct attribute {
...@@ -32,20 +24,21 @@ struct attribute { ...@@ -32,20 +24,21 @@ struct attribute {
}; };
extern int extern int
sysfs_create_dir(struct driver_dir_entry *, struct driver_dir_entry *); sysfs_create_dir(struct kobject *);
extern void extern void
sysfs_remove_dir(struct driver_dir_entry * entry); sysfs_remove_dir(struct kobject *);
extern int extern int
sysfs_create_file(struct attribute * attr, sysfs_create_file(struct kobject *, struct attribute *);
struct driver_dir_entry * parent);
extern void
sysfs_remove_file(struct kobject *, struct attribute *);
extern int extern int
sysfs_create_symlink(struct driver_dir_entry * parent, sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name);
char * name, char * target);
extern void extern void
sysfs_remove_file(struct driver_dir_entry *, const char * name); sysfs_remove_link(struct kobject *, char * name);
#endif /* _SYSFS_H_ */ #endif /* _SYSFS_H_ */
...@@ -9,10 +9,11 @@ ...@@ -9,10 +9,11 @@
L_TARGET := lib.a L_TARGET := lib.a
export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o \ export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o \
crc32.o rbtree.o radix-tree.o crc32.o rbtree.o radix-tree.o kobject.o
obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o \ obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o \
bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \
kobject.o
obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
......
/*
* kobject.c - library routines for handling generic kernel objects
*/
#define DEBUG 1
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/stat.h>
/**
* kobject_populate_dir - populate directory with attributes.
* @kobj: object we're working on.
*
* Most subsystems have a set of default attributes that
* are associated with an object that registers with them.
* This is a helper called during object registration that
* loops through the default attributes of the subsystem
* and creates attributes files for them in sysfs.
*
*/
static int kobject_populate_dir(struct kobject * kobj)
{
struct subsystem * s = kobj->subsys;
struct attribute * attr;
int error = 0;
int i;
if (s && s->default_attrs) {
for (i = 0; (attr = s->default_attrs[i]); i++) {
if ((error = sysfs_create_file(kobj,attr)))
break;
}
}
return error;
}
/**
* kobject_init - initialize object.
* @kobj: object in question.
*/
void kobject_init(struct kobject * kobj)
{
atomic_set(&kobj->refcount,1);
INIT_LIST_HEAD(&kobj->entry);
}
/**
* kobject_register - register an object.
* @kobj: object in question.
*
* For now, fill in the replicated fields in the object's
* directory entry, and create a dir in sysfs.
* This stuff should go away in the future, as we move
* more implicit things to sysfs.
*/
int kobject_register(struct kobject * kobj)
{
int error = 0;
struct subsystem * s = subsys_get(kobj->subsys);
struct kobject * parent = kobject_get(kobj->parent);
pr_debug("kobject %s: registering\n",kobj->name);
if (parent)
pr_debug(" parent is %s\n",parent->name);
if (s) {
down_write(&s->rwsem);
if (parent)
list_add_tail(&kobj->entry,&parent->entry);
else {
list_add_tail(&kobj->entry,&s->list);
kobj->parent = &s->kobj;
}
up_write(&s->rwsem);
}
error = sysfs_create_dir(kobj);
if (!error) {
error = kobject_populate_dir(kobj);
if (error)
sysfs_remove_dir(kobj);
}
return error;
}
/**
* kobject_unregister - unlink an object.
* @kobj: object going away.
*
* The device has been told to be removed, but may
* not necessarily be disappearing from the kernel.
* So, we remove the directory and decrement the refcount
* that we set with kobject_register().
*
* Eventually (maybe now), the refcount will hit 0, and
* put_device() will clean the device up.
*/
void kobject_unregister(struct kobject * kobj)
{
pr_debug("kobject %s: unregistering\n",kobj->name);
sysfs_remove_dir(kobj);
if (kobj->subsys) {
down_write(&kobj->subsys->rwsem);
list_del_init(&kobj->entry);
up_write(&kobj->subsys->rwsem);
}
kobject_put(kobj);
}
/**
* kobject_get - increment refcount for object.
* @kobj: object.
*/
struct kobject * kobject_get(struct kobject * kobj)
{
struct kobject * ret = kobj;
if (kobj && atomic_read(&kobj->refcount) > 0)
atomic_inc(&kobj->refcount);
else
ret = NULL;
return ret;
}
/**
* kobject_put - decrement refcount for object.
* @kobj: object.
*
* Decrement the refcount, and check if 0. If it is, then
* we're gonna need to clean it up, and decrement the refcount
* of its parent.
*
* @kobj->parent could point to its subsystem, which we also
* want to decrement the reference count for. We always dec
* the refcount for the parent, but only do so for the subsystem
* if it points to a different place than the parent.
*/
void kobject_put(struct kobject * kobj)
{
struct kobject * parent = kobj->parent;
struct subsystem * s = kobj->subsys;
if (!atomic_dec_and_test(&kobj->refcount))
return;
pr_debug("kobject %s: cleaning up\n",kobj->name);
if (s) {
if (s->release)
s->release(kobj);
if (&s->kobj != parent)
subsys_put(s);
}
if (parent)
kobject_put(parent);
}
void subsystem_init(struct subsystem * s)
{
kobject_init(&s->kobj);
init_rwsem(&s->rwsem);
INIT_LIST_HEAD(&s->list);
}
/**
* subsystem_register - register a subsystem.
* @s: the subsystem we're registering.
*/
int subsystem_register(struct subsystem * s)
{
subsystem_init(s);
if (s->parent)
s->kobj.parent = &s->parent->kobj;
pr_debug("subsystem %s: registering\n",s->kobj.name);
if (s->parent)
pr_debug(" parent is %s\n",s->parent->kobj.name);
return kobject_register(&s->kobj);
}
void subsystem_unregister(struct subsystem * s)
{
pr_debug("subsystem %s: unregistering\n",s->kobj.name);
kobject_unregister(&s->kobj);
}
EXPORT_SYMBOL(kobject_init);
EXPORT_SYMBOL(kobject_register);
EXPORT_SYMBOL(kobject_unregister);
EXPORT_SYMBOL(kobject_get);
EXPORT_SYMBOL(kobject_put);
EXPORT_SYMBOL(subsystem_init);
EXPORT_SYMBOL(subsystem_register);
EXPORT_SYMBOL(subsystem_unregister);
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