Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 59146
b: refs/heads/master
c: fb6896d
h: refs/heads/master
v: v3
  • Loading branch information
Tejun Heo authored and Greg Kroah-Hartman committed Jul 11, 2007
1 parent 8ef1833 commit 2ecdd8c
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 125 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 3007e997de91ec59af39a3f9c91595b31ae6e08b
refs/heads/master: fb6896da37f19be4b75154c14d1cd79231255b17
250 changes: 186 additions & 64 deletions trunk/fs/sysfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ static DEFINE_IDA(sysfs_ino_ida);
* Locking:
* mutex_lock(sysfs_mutex)
*/
static void sysfs_link_sibling(struct sysfs_dirent *sd)
void sysfs_link_sibling(struct sysfs_dirent *sd)
{
struct sysfs_dirent *parent_sd = sd->s_parent;

Expand All @@ -49,7 +49,7 @@ static void sysfs_link_sibling(struct sysfs_dirent *sd)
* Locking:
* mutex_lock(sysfs_mutex)
*/
static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
void sysfs_unlink_sibling(struct sysfs_dirent *sd)
{
struct sysfs_dirent **pos;

Expand Down Expand Up @@ -165,7 +165,7 @@ void sysfs_put_active_two(struct sysfs_dirent *sd)
*
* Deny new active references and drain existing ones.
*/
void sysfs_deactivate(struct sysfs_dirent *sd)
static void sysfs_deactivate(struct sysfs_dirent *sd)
{
DECLARE_COMPLETION_ONSTACK(wait);
int v;
Expand Down Expand Up @@ -318,27 +318,164 @@ static void sysfs_attach_dentry(struct sysfs_dirent *sd, struct dentry *dentry)
d_rehash(dentry);
}

static int sysfs_ilookup_test(struct inode *inode, void *arg)
{
struct sysfs_dirent *sd = arg;
return inode->i_ino == sd->s_ino;
}

/**
* sysfs_attach_dirent - attach sysfs_dirent to its parent and dentry
* @sd: sysfs_dirent to attach
* @parent_sd: parent to attach to (optional)
* @dentry: dentry to be associated to @sd (optional)
* sysfs_addrm_start - prepare for sysfs_dirent add/remove
* @acxt: pointer to sysfs_addrm_cxt to be used
* @parent_sd: parent sysfs_dirent
*
* Attach @sd to @parent_sd and/or @dentry. Both are optional.
* This function is called when the caller is about to add or
* remove sysfs_dirent under @parent_sd. This function acquires
* sysfs_mutex, grabs inode for @parent_sd if available and lock
* i_mutex of it. @acxt is used to keep and pass context to
* other addrm functions.
*
* LOCKING:
* mutex_lock(sysfs_mutex)
* Kernel thread context (may sleep). sysfs_mutex is locked on
* return. i_mutex of parent inode is locked on return if
* available.
*/
void sysfs_attach_dirent(struct sysfs_dirent *sd,
struct sysfs_dirent *parent_sd, struct dentry *dentry)
void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
struct sysfs_dirent *parent_sd)
{
if (dentry)
sysfs_attach_dentry(sd, dentry);
struct inode *inode;

if (parent_sd) {
sd->s_parent = sysfs_get(parent_sd);
sysfs_link_sibling(sd);
memset(acxt, 0, sizeof(*acxt));
acxt->parent_sd = parent_sd;

/* Lookup parent inode. inode initialization and I_NEW
* clearing are protected by sysfs_mutex. By grabbing it and
* looking up with _nowait variant, inode state can be
* determined reliably.
*/
mutex_lock(&sysfs_mutex);

inode = ilookup5_nowait(sysfs_sb, parent_sd->s_ino, sysfs_ilookup_test,
parent_sd);

if (inode && !(inode->i_state & I_NEW)) {
/* parent inode available */
acxt->parent_inode = inode;

/* sysfs_mutex is below i_mutex in lock hierarchy.
* First, trylock i_mutex. If fails, unlock
* sysfs_mutex and lock them in order.
*/
if (!mutex_trylock(&inode->i_mutex)) {
mutex_unlock(&sysfs_mutex);
mutex_lock(&inode->i_mutex);
mutex_lock(&sysfs_mutex);
}
} else
iput(inode);
}

/**
* sysfs_add_one - add sysfs_dirent to parent
* @acxt: addrm context to use
* @sd: sysfs_dirent to be added
*
* Get @acxt->parent_sd and set sd->s_parent to it and increment
* nlink of parent inode if @sd is a directory. @sd is NOT
* linked into the children list of the parent. The caller
* should invoke sysfs_link_sibling() after this function
* completes if @sd needs to be on the children list.
*
* This function should be called between calls to
* sysfs_addrm_start() and sysfs_addrm_finish() and should be
* passed the same @acxt as passed to sysfs_addrm_start().
*
* LOCKING:
* Determined by sysfs_addrm_start().
*/
void sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
{
sd->s_parent = sysfs_get(acxt->parent_sd);

if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
inc_nlink(acxt->parent_inode);

acxt->cnt++;
}

/**
* sysfs_remove_one - remove sysfs_dirent from parent
* @acxt: addrm context to use
* @sd: sysfs_dirent to be added
*
* Mark @sd removed and drop nlink of parent inode if @sd is a
* directory. @sd is NOT unlinked from the children list of the
* parent. The caller is repsonsible for removing @sd from the
* children list before calling this function.
*
* This function should be called between calls to
* sysfs_addrm_start() and sysfs_addrm_finish() and should be
* passed the same @acxt as passed to sysfs_addrm_start().
*
* LOCKING:
* Determined by sysfs_addrm_start().
*/
void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
{
BUG_ON(sd->s_sibling || (sd->s_flags & SYSFS_FLAG_REMOVED));

sd->s_flags |= SYSFS_FLAG_REMOVED;
sd->s_sibling = acxt->removed;
acxt->removed = sd;

if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
drop_nlink(acxt->parent_inode);

acxt->cnt++;
}

/**
* sysfs_addrm_finish - finish up sysfs_dirent add/remove
* @acxt: addrm context to finish up
*
* Finish up sysfs_dirent add/remove. Resources acquired by
* sysfs_addrm_start() are released and removed sysfs_dirents are
* cleaned up. Timestamps on the parent inode are updated.
*
* LOCKING:
* All mutexes acquired by sysfs_addrm_start() are released.
*
* RETURNS:
* Number of added/removed sysfs_dirents since sysfs_addrm_start().
*/
int sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
{
/* release resources acquired by sysfs_addrm_start() */
mutex_unlock(&sysfs_mutex);
if (acxt->parent_inode) {
struct inode *inode = acxt->parent_inode;

/* if added/removed, update timestamps on the parent */
if (acxt->cnt)
inode->i_ctime = inode->i_mtime = CURRENT_TIME;

mutex_unlock(&inode->i_mutex);
iput(inode);
}

/* kill removed sysfs_dirents */
while (acxt->removed) {
struct sysfs_dirent *sd = acxt->removed;

acxt->removed = sd->s_sibling;
sd->s_sibling = NULL;

sysfs_drop_dentry(sd);
sysfs_deactivate(sd);
sysfs_put(sd);
}

return acxt->cnt;
}

/**
Expand Down Expand Up @@ -396,19 +533,20 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
const char *name, struct sysfs_dirent **p_sd)
{
struct dentry *parent = parent_sd->s_dentry;
struct sysfs_addrm_cxt acxt;
int error;
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
struct dentry *dentry;
struct inode *inode;
struct sysfs_dirent *sd;

mutex_lock(&parent->d_inode->i_mutex);
sysfs_addrm_start(&acxt, parent_sd);

/* allocate */
dentry = lookup_one_len(name, parent, strlen(name));
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
goto out_unlock;
goto out_finish;
}

error = -EEXIST;
Expand All @@ -433,23 +571,18 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
}

/* link in */
mutex_lock(&sysfs_mutex);

error = -EEXIST;
if (sysfs_find_dirent(parent_sd, name)) {
mutex_unlock(&sysfs_mutex);
if (sysfs_find_dirent(parent_sd, name))
goto out_iput;
}

sysfs_add_one(&acxt, sd);
sysfs_link_sibling(sd);
sysfs_instantiate(dentry, inode);
inc_nlink(parent->d_inode);
sysfs_attach_dirent(sd, parent_sd, dentry);

mutex_unlock(&sysfs_mutex);
sysfs_attach_dentry(sd, dentry);

*p_sd = sd;
error = 0;
goto out_unlock; /* pin directory dentry in core */
goto out_finish; /* pin directory dentry in core */

out_iput:
iput(inode);
Expand All @@ -459,8 +592,8 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
d_drop(dentry);
out_dput:
dput(dentry);
out_unlock:
mutex_unlock(&parent->d_inode->i_mutex);
out_finish:
sysfs_addrm_finish(&acxt);
return error;
}

Expand Down Expand Up @@ -561,16 +694,12 @@ const struct inode_operations sysfs_dir_inode_operations = {

static void remove_dir(struct sysfs_dirent *sd)
{
mutex_lock(&sysfs_mutex);
sysfs_unlink_sibling(sd);
sd->s_flags |= SYSFS_FLAG_REMOVED;
mutex_unlock(&sysfs_mutex);
struct sysfs_addrm_cxt acxt;

pr_debug(" o %s removing done\n", sd->s_name);

sysfs_drop_dentry(sd);
sysfs_deactivate(sd);
sysfs_put(sd);
sysfs_addrm_start(&acxt, sd->s_parent);
sysfs_unlink_sibling(sd);
sysfs_remove_one(&acxt, sd);
sysfs_addrm_finish(&acxt);
}

void sysfs_remove_subdir(struct sysfs_dirent *sd)
Expand All @@ -581,38 +710,26 @@ void sysfs_remove_subdir(struct sysfs_dirent *sd)

static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
{
struct sysfs_dirent *removed = NULL;
struct sysfs_addrm_cxt acxt;
struct sysfs_dirent **pos;

if (!dir_sd)
return;

pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
mutex_lock(&sysfs_mutex);
sysfs_addrm_start(&acxt, dir_sd);
pos = &dir_sd->s_children;
while (*pos) {
struct sysfs_dirent *sd = *pos;

if (sysfs_type(sd) && (sysfs_type(sd) & SYSFS_NOT_PINNED)) {
sd->s_flags |= SYSFS_FLAG_REMOVED;
*pos = sd->s_sibling;
sd->s_sibling = removed;
removed = sd;
sd->s_sibling = NULL;
sysfs_remove_one(&acxt, sd);
} else
pos = &(*pos)->s_sibling;
}
mutex_unlock(&sysfs_mutex);

while (removed) {
struct sysfs_dirent *sd = removed;

removed = sd->s_sibling;
sd->s_sibling = NULL;

sysfs_drop_dentry(sd);
sysfs_deactivate(sd);
sysfs_put(sd);
}
sysfs_addrm_finish(&acxt);

remove_dir(dir_sd);
}
Expand Down Expand Up @@ -772,7 +889,8 @@ static int sysfs_dir_open(struct inode *inode, struct file *file)
sd = sysfs_new_dirent("_DIR_", 0, 0);
if (sd) {
mutex_lock(&sysfs_mutex);
sysfs_attach_dirent(sd, parent_sd, NULL);
sd->s_parent = sysfs_get(parent_sd);
sysfs_link_sibling(sd);
mutex_unlock(&sysfs_mutex);
}

Expand Down Expand Up @@ -957,6 +1075,7 @@ struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj)
struct sysfs_dirent *parent_sd = parent->d_fsdata;
struct dentry *shadow;
struct sysfs_dirent *sd;
struct sysfs_addrm_cxt acxt;

sd = ERR_PTR(-EINVAL);
if (!sysfs_is_shadowed_inode(inode))
Expand All @@ -970,15 +1089,18 @@ struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj)
if (!sd)
goto nomem;
sd->s_elem.dir.kobj = kobj;
/* point to parent_sd but don't attach to it */
sd->s_parent = sysfs_get(parent_sd);
mutex_lock(&sysfs_mutex);
sysfs_attach_dirent(sd, NULL, shadow);
mutex_unlock(&sysfs_mutex);

sysfs_addrm_start(&acxt, parent_sd);

/* add but don't link into children list */
sysfs_add_one(&acxt, sd);

/* attach and instantiate dentry */
sysfs_attach_dentry(sd, shadow);
d_instantiate(shadow, igrab(inode));
inc_nlink(inode);
inc_nlink(parent->d_inode);
inc_nlink(inode); /* tj: synchronization? */

sysfs_addrm_finish(&acxt);

dget(shadow); /* Extra count - pin the dentry in core */

Expand Down
Loading

0 comments on commit 2ecdd8c

Please sign in to comment.