Commit 51225039 authored by Tejun Heo's avatar Tejun Heo Committed by Greg Kroah-Hartman

sysfs: make directory dentries and inodes reclaimable

This patch makes dentries and inodes for sysfs directories
reclaimable.

* sysfs_notify() is modified to walk sysfs_dirent tree instead of
  dentry tree.

* sysfs_update_file() and sysfs_chmod_file() use sysfs_get_dentry() to
  grab the victim dentry.

* sysfs_rename_dir() and sysfs_move_dir() grab all dentries using
  sysfs_get_dentry() on startup.

* Dentries for all shadowed directories are pinned in memory to serve
  as lookup start point.
Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 53e0ae92
This diff is collapsed.
......@@ -362,43 +362,22 @@ static unsigned int sysfs_poll(struct file *filp, poll_table *wait)
return POLLERR|POLLPRI;
}
static struct dentry *step_down(struct dentry *dir, const char * name)
void sysfs_notify(struct kobject *k, char *dir, char *attr)
{
struct dentry * de;
if (dir == NULL || dir->d_inode == NULL)
return NULL;
mutex_lock(&dir->d_inode->i_mutex);
de = lookup_one_len(name, dir, strlen(name));
mutex_unlock(&dir->d_inode->i_mutex);
dput(dir);
if (IS_ERR(de))
return NULL;
if (de->d_inode == NULL) {
dput(de);
return NULL;
}
return de;
}
struct sysfs_dirent *sd = k->sd;
void sysfs_notify(struct kobject * k, char *dir, char *attr)
{
struct dentry *de = k->sd->s_dentry;
if (de)
dget(de);
if (de && dir)
de = step_down(de, dir);
if (de && attr)
de = step_down(de, attr);
if (de) {
struct sysfs_dirent * sd = de->d_fsdata;
if (sd)
atomic_inc(&sd->s_event);
mutex_lock(&sysfs_mutex);
if (sd && dir)
sd = sysfs_find_dirent(sd, dir);
if (sd && attr)
sd = sysfs_find_dirent(sd, attr);
if (sd) {
atomic_inc(&sd->s_event);
wake_up_interruptible(&k->poll);
dput(de);
}
mutex_unlock(&sysfs_mutex);
}
EXPORT_SYMBOL_GPL(sysfs_notify);
......@@ -485,30 +464,31 @@ EXPORT_SYMBOL_GPL(sysfs_add_file_to_group);
*/
int sysfs_update_file(struct kobject * kobj, const struct attribute * attr)
{
struct dentry *dir = kobj->sd->s_dentry;
struct dentry * victim;
int res = -ENOENT;
mutex_lock(&dir->d_inode->i_mutex);
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);
res = 0;
} else
d_drop(victim);
/**
* Drop the reference acquired from lookup_one_len() above.
*/
dput(victim);
struct sysfs_dirent *victim_sd = NULL;
struct dentry *victim = NULL;
int rc;
rc = -ENOENT;
victim_sd = sysfs_get_dirent(kobj->sd, attr->name);
if (!victim_sd)
goto out;
victim = sysfs_get_dentry(victim_sd);
if (IS_ERR(victim)) {
rc = PTR_ERR(victim);
victim = NULL;
goto out;
}
mutex_unlock(&dir->d_inode->i_mutex);
return res;
mutex_lock(&victim->d_inode->i_mutex);
victim->d_inode->i_mtime = CURRENT_TIME;
fsnotify_modify(victim);
mutex_unlock(&victim->d_inode->i_mutex);
rc = 0;
out:
dput(victim);
sysfs_put(victim_sd);
return rc;
}
......@@ -521,30 +501,34 @@ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr)
*/
int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode)
{
struct dentry *dir = kobj->sd->s_dentry;
struct dentry *victim;
struct sysfs_dirent *victim_sd = NULL;
struct dentry *victim = NULL;
struct inode * inode;
struct iattr newattrs;
int res = -ENOENT;
mutex_lock(&dir->d_inode->i_mutex);
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;
mutex_lock(&inode->i_mutex);
newattrs.ia_mode = (mode & S_IALLUGO) |
(inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
res = notify_change(victim, &newattrs);
mutex_unlock(&inode->i_mutex);
}
dput(victim);
int rc;
rc = -ENOENT;
victim_sd = sysfs_get_dirent(kobj->sd, attr->name);
if (!victim_sd)
goto out;
victim = sysfs_get_dentry(victim_sd);
if (IS_ERR(victim)) {
rc = PTR_ERR(victim);
victim = NULL;
goto out;
}
mutex_unlock(&dir->d_inode->i_mutex);
return res;
inode = victim->d_inode;
mutex_lock(&inode->i_mutex);
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
rc = notify_change(victim, &newattrs);
mutex_unlock(&inode->i_mutex);
out:
dput(victim);
sysfs_put(victim_sd);
return rc;
}
EXPORT_SYMBOL_GPL(sysfs_chmod_file);
......
......@@ -24,7 +24,7 @@ static const struct super_operations sysfs_ops = {
.drop_inode = sysfs_delete_inode,
};
static struct sysfs_dirent sysfs_root = {
struct sysfs_dirent sysfs_root = {
.s_count = ATOMIC_INIT(1),
.s_flags = SYSFS_ROOT,
.s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
......
......@@ -52,6 +52,7 @@ struct sysfs_addrm_cxt {
};
extern struct vfsmount * sysfs_mount;
extern struct sysfs_dirent sysfs_root;
extern struct kmem_cache *sysfs_dir_cachep;
extern struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd);
......
......@@ -81,7 +81,6 @@ struct sysfs_ops {
#define SYSFS_KOBJ_ATTR 0x0004
#define SYSFS_KOBJ_BIN_ATTR 0x0008
#define SYSFS_KOBJ_LINK 0x0020
#define SYSFS_NOT_PINNED (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR | SYSFS_KOBJ_LINK)
#define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK)
#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK
......
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