From 0c096b507f15397da890051ee73de4266d3941fb Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:15 +0900 Subject: sysfs: add sysfs_dirent->s_name Add s_name to sysfs_dirent. This is to further reduce dependency to the associated dentry. Name is copied for directories and symlinks but not for attributes. Where possible, name dereferences are converted to use sd->s_name. sysfs_symlink->link_name and sysfs_get_name() are unused now and removed. This change allows symlink to be implemented using sysfs_dirent tree proper, which is the last remaining dentry-dependent sysfs walk. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 5266eec15f6..5c605b0003a 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -191,37 +191,6 @@ int sysfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) return error; } -/* - * Get the name for corresponding element represented by the given sysfs_dirent - */ -const unsigned char * sysfs_get_name(struct sysfs_dirent *sd) -{ - struct attribute * attr; - struct bin_attribute * bin_attr; - struct sysfs_symlink * sl; - - BUG_ON(!sd || !sd->s_element); - - 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; -} - static inline void orphan_all_buffers(struct inode *node) { struct sysfs_buffer_collection *set; @@ -305,7 +274,7 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { if (!sd->s_element) continue; - if (!strcmp(sysfs_get_name(sd), name)) { + if (!strcmp(sd->s_name, name)) { list_del_init(&sd->s_sibling); sysfs_drop_dentry(sd, dir); sysfs_put(sd); -- cgit v1.2.3 From 3e5190380ebef77f2b015c9e7a4ca225a3d75021 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:15 +0900 Subject: sysfs: make sysfs_dirent->s_element a union Make sd->s_element a union of sysfs_elem_{dir|symlink|attr|bin_attr} and rename it to s_elem. This is to achieve... * some level of type checking : changing symlink to point to sysfs_dirent instead of kobject is much safer and less painful now. * easier / standardized dereferencing * allow sysfs_elem_* to contain more than one entry Where possible, pointer is obtained by directly deferencing from sd instead of going through other entities. This reduces dependencies to dentry, inode and kobject. to_attr() and to_bin_attr() are unused now and removed. This is in preparation of object reference simplification. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 5c605b0003a..617d10cea07 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -272,7 +272,7 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) parent_sd = dir->d_fsdata; mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { - if (!sd->s_element) + if (!sd->s_type) continue; if (!strcmp(sd->s_name, name)) { list_del_init(&sd->s_sibling); -- cgit v1.2.3 From 0ab66088c855eca68513bdd7442a426c4b374ced Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:16 +0900 Subject: sysfs: implement sysfs_dirent active reference and immediate disconnect sysfs: implement sysfs_dirent active reference and immediate disconnect Opening a sysfs node references its associated kobject, so userland can arbitrarily prolong lifetime of a kobject which complicates lifetime rules in drivers. This patch implements active reference and makes the association between kobject and sysfs immediately breakable. Now each sysfs_dirent has two reference counts - s_count and s_active. s_count is a regular reference count which guarantees that the containing sysfs_dirent is accessible. As long as s_count reference is held, all sysfs internal fields in sysfs_dirent are accessible including s_parent and s_name. The newly added s_active is active reference count. This is acquired by invoking sysfs_get_active() and it's the caller's responsibility to ensure sysfs_dirent itself is accessible (should be holding s_count one way or the other). Dereferencing sysfs_dirent to access objects out of sysfs proper requires active reference. This includes access to the associated kobjects, attributes and ops. The active references can be drained and denied by calling sysfs_deactivate(). All active sysfs_dirents must be deactivated after deletion but before the default reference is dropped. This enables immediate disconnect of sysfs nodes. Once a sysfs_dirent is deleted, it won't access any entity external to sysfs proper. Because attr/bin_attr ops access both the node itself and its parent for kobject, they need to hold active references to both. sysfs_get/put_active_two() helpers are provided to help grabbing both references. Parent's is acquired first and released last. Unlike other operations, mmapped area lingers on after mmap() is finished and the module implement implementing it and kobj need to stay referenced till all the mapped pages are gone. This is accomplished by holding one set of active references to the bin_attr and its parent if there have been any mmap during lifetime of an openfile. The references are dropped when the openfile is released. This change makes sysfs lifetime rules independent from both kobject's and module's. It not only fixes several race conditions caused by sysfs not holding onto the proper module when referencing kobject, but also helps fixing and simplifying lifetime management in driver model and drivers by taking sysfs out of the equation. Please read the following message for more info. http://article.gmane.org/gmane.linux.kernel/510293 Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 617d10cea07..7b9a8f132d5 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -277,12 +277,16 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) if (!strcmp(sd->s_name, name)) { list_del_init(&sd->s_sibling); sysfs_drop_dentry(sd, dir); - sysfs_put(sd); found = 1; break; } } mutex_unlock(&dir->d_inode->i_mutex); - return found ? 0 : -ENOENT; + if (!found) + return -ENOENT; + + sysfs_deactivate(sd); + sysfs_put(sd); + return 0; } -- cgit v1.2.3 From 73107cb3ad3963c0f929ae681c05081eafb1c079 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:16 +0900 Subject: sysfs: kill attribute file orphaning Now that sysfs_dirent can be disconnected from kobject on deletion, there is no need to orphan each attribute files. All [bin_]attribute nodes are automatically orphaned when the parent node is deleted. Kill attribute file orphaning. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 7b9a8f132d5..d9ccc830b73 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -191,24 +191,6 @@ int sysfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) return error; } -static inline void orphan_all_buffers(struct inode *node) -{ - struct sysfs_buffer_collection *set; - struct sysfs_buffer *buf; - - mutex_lock_nested(&node->i_mutex, I_MUTEX_CHILD); - set = node->i_private; - if (set) { - list_for_each_entry(buf, &set->associates, associates) { - down(&buf->sem); - buf->orphaned = 1; - up(&buf->sem); - } - } - mutex_unlock(&node->i_mutex); -} - - /* * Unhashes the dentry corresponding to given sysfs_dirent * Called with parent inode's i_mutex held. @@ -216,7 +198,6 @@ static inline void orphan_all_buffers(struct inode *node) void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) { struct dentry *dentry = NULL; - struct inode *inode; /* We're not holding a reference to ->s_dentry dentry but the * field will stay valid as long as sysfs_lock is held. @@ -236,17 +217,11 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) spin_lock(&dcache_lock); spin_lock(&dentry->d_lock); if (!d_unhashed(dentry) && dentry->d_inode) { - inode = dentry->d_inode; - spin_lock(&inode->i_lock); - __iget(inode); - spin_unlock(&inode->i_lock); dget_locked(dentry); __d_drop(dentry); spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); simple_unlink(parent->d_inode, dentry); - orphan_all_buffers(inode); - iput(inode); } else { spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); -- cgit v1.2.3 From 198a2a847015805c6f57d8cc732bdaaccb494007 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:16 +0900 Subject: sysfs: separate out sysfs_attach_dentry() Consolidate sd <-> dentry association into sysfs_attach_dentry() and call it after dentry and inode are properly set up. This is in preparation of sysfs_drop_dentry() updates. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index d9ccc830b73..88857a399d0 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -156,13 +156,13 @@ struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent * sd) return inode; } -int sysfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) +int sysfs_create(struct sysfs_dirent *sd, 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; -- cgit v1.2.3 From dbde0fcf9f8f6d477af3c32d9979e789ee680cde Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:16 +0900 Subject: sysfs: reimplement sysfs_drop_dentry() This patch reimplements sysfs_drop_dentry() such that remove_dir() can use it to drop dentry instead of using a separate mechanism. With this change, making directories reclaimable is much easier. This patch used to contain fixes for two race conditions around sd->s_dentry but that part has been separated out and included into mainline early as commit 6aa054aadfea613a437ad0b15d38eca2b963fc0a and dd14cbc994709a1c5a64ed3621f583c49a27e521. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 82 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 21 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 88857a399d0..6ad47c13b94 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -191,13 +191,25 @@ int sysfs_create(struct sysfs_dirent *sd, struct dentry *dentry, int mode, return error; } -/* - * Unhashes the dentry corresponding to given sysfs_dirent - * Called with parent inode's i_mutex held. +/** + * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent + * @sd: target sysfs_dirent + * + * Drop dentry for @sd. @sd must have been unlinked from its + * parent on entry to this function such that it can't be looked + * up anymore. + * + * @sd->s_dentry which is protected with sysfs_lock points to the + * currently associated dentry but we're not holding a reference + * to it and racing with dput(). Grab dcache_lock and verify + * dentry before dropping it. If @sd->s_dentry is NULL or dput() + * beats us, no need to bother. */ -void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) +void sysfs_drop_dentry(struct sysfs_dirent *sd) { - struct dentry *dentry = NULL; + struct dentry *dentry = NULL, *parent = NULL; + struct inode *dir; + struct timespec curtime; /* We're not holding a reference to ->s_dentry dentry but the * field will stay valid as long as sysfs_lock is held. @@ -205,30 +217,57 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) spin_lock(&sysfs_lock); spin_lock(&dcache_lock); - /* dget dentry if it's still alive */ - if (sd->s_dentry && sd->s_dentry->d_inode) + if (sd->s_dentry && sd->s_dentry->d_inode) { + /* get dentry if it's there and dput() didn't kill it yet */ dentry = dget_locked(sd->s_dentry); + parent = dentry->d_parent; + } else if (sd->s_parent->s_dentry->d_inode) { + /* We need to update the parent even if dentry for the + * victim itself doesn't exist. + */ + parent = dget_locked(sd->s_parent->s_dentry); + } + + /* drop */ + if (dentry) { + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); + } spin_unlock(&dcache_lock); spin_unlock(&sysfs_lock); - /* drop dentry */ + /* nothing to do if the parent isn't in dcache */ + if (!parent) + return; + + /* adjust nlink and update timestamp */ + dir = parent->d_inode; + mutex_lock(&dir->i_mutex); + + curtime = CURRENT_TIME; + + dir->i_ctime = dir->i_mtime = curtime; + 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); + dentry->d_inode->i_ctime = curtime; + drop_nlink(dentry->d_inode); + if (sd->s_type & SYSFS_DIR) { + drop_nlink(dentry->d_inode); + drop_nlink(dir); + /* XXX: unpin if directory, this will go away soon */ + dput(dentry); } + } + + mutex_unlock(&dir->i_mutex); + /* bye bye */ + if (dentry) dput(dentry); - } + else + dput(parent); } int sysfs_hash_and_remove(struct dentry * dir, const char * name) @@ -251,7 +290,6 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) continue; if (!strcmp(sd->s_name, name)) { list_del_init(&sd->s_sibling); - sysfs_drop_dentry(sd, dir); found = 1; break; } @@ -261,7 +299,9 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) if (!found) return -ENOENT; + sysfs_drop_dentry(sd); sysfs_deactivate(sd); sysfs_put(sd); + return 0; } -- cgit v1.2.3 From fc9f54b9982e14e6dbe023425c87ffbfd6992c45 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:17 +0900 Subject: sysfs: reorganize sysfs_new_indoe() and sysfs_create() Reorganize/clean up sysfs_new_inode() and sysfs_create(). * sysfs_init_inode() is separated out from sysfs_new_inode() and is responsible for basic initialization. * sysfs_instantiate() replaces the last step of sysfs_create() and is responsible for dentry instantitaion. * type-specific initialization is moved out to the callers. * mode is specified only once when creating a sysfs_dirent. * spurious list_del_init(&sd->s_sibling) dropped from create_dir() This change is to * prepare for inode allocation fix. * separate alloc and init code for synchronization update. * make dentry/inode initialization more flexible for later changes. This patch doesn't introduce visible behavior change. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 108 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 51 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 6ad47c13b94..26d8503c899 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -133,62 +133,68 @@ static inline void set_inode_attr(struct inode * inode, struct iattr * iattr) */ static struct lock_class_key sysfs_inode_imutex_key; -struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent * sd) +void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) { - struct inode * inode = new_inode(sysfs_sb); - if (inode) { - 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; - inode->i_ino = sd->s_ino; - lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); - - if (sd->s_iattr) { - /* sysfs_dirent has non-default attributes - * get them for the new inode from persistent copy - * in sysfs_dirent - */ - set_inode_attr(inode, sd->s_iattr); - } else - set_default_inode_attr(inode, mode); - } + 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; + inode->i_ino = sd->s_ino; + lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); + + if (sd->s_iattr) { + /* sysfs_dirent has non-default attributes + * get them for the new inode from persistent copy + * in sysfs_dirent + */ + set_inode_attr(inode, sd->s_iattr); + } else + set_default_inode_attr(inode, sd->s_mode); +} + +/** + * sysfs_new_inode - allocate new inode for sysfs_dirent + * @sd: sysfs_dirent to allocate inode for + * + * Allocate inode for @sd and initialize basics. + * + * LOCKING: + * Kernel thread context (may sleep). + * + * RETURNS: + * Pointer to allocated inode on success, NULL on failure. + */ +struct inode * sysfs_new_inode(struct sysfs_dirent *sd) +{ + struct inode *inode; + + inode = new_inode(sysfs_sb); + if (inode) + sysfs_init_inode(sd, inode); + return inode; } -int sysfs_create(struct sysfs_dirent *sd, struct dentry *dentry, int mode, - int (*init)(struct inode *)) +/** + * sysfs_instantiate - instantiate dentry + * @dentry: dentry to be instantiated + * @inode: inode associated with @sd + * + * Instantiate @dentry with @inode. + * + * LOCKING: + * None. + */ +void sysfs_instantiate(struct dentry *dentry, struct inode *inode) { - int error = 0; - struct inode * inode = NULL; - if (dentry) { - if (!dentry->d_inode) { - 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)) - dget(dentry); /* pin only directory dentry in core */ - } else - iput(inode); - Done: - return error; + BUG_ON(!dentry || dentry->d_inode); + + 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; + } + + d_instantiate(dentry, inode); } /** -- cgit v1.2.3 From 8312a8d7c1d19d31027bd4ca127ce671962c23d4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:17 +0900 Subject: sysfs: use iget_locked() instead of new_inode() After dentry is reclaimed, sysfs always used to allocate new dentry and inode if the file is accessed again. This causes problem with operations which only pin the inode. For example, if inotify watch is added to a sysfs file and the dentry for the file is reclaimed, the next update event creates new dentry and new inode making the inotify watch miss all the events from there on. This patch fixes it by using iget_locked() instead of new_inode(). sysfs_new_inode() is renamed to sysfs_get_inode() and inode is initialized iff the inode is newly allocated. sysfs_instantiate() is responsible for unlocking new inodes. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 26d8503c899..3eab9c46a71 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -153,10 +153,12 @@ void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) } /** - * sysfs_new_inode - allocate new inode for sysfs_dirent + * sysfs_get_inode - get inode for sysfs_dirent * @sd: sysfs_dirent to allocate inode for * - * Allocate inode for @sd and initialize basics. + * Get inode for @sd. If such inode doesn't exist, a new inode + * is allocated and basics are initialized. New inode is + * returned locked. * * LOCKING: * Kernel thread context (may sleep). @@ -164,12 +166,12 @@ void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) * RETURNS: * Pointer to allocated inode on success, NULL on failure. */ -struct inode * sysfs_new_inode(struct sysfs_dirent *sd) +struct inode * sysfs_get_inode(struct sysfs_dirent *sd) { struct inode *inode; - inode = new_inode(sysfs_sb); - if (inode) + inode = iget_locked(sysfs_sb, sd->s_ino); + if (inode && (inode->i_state & I_NEW)) sysfs_init_inode(sd, inode); return inode; @@ -180,7 +182,7 @@ struct inode * sysfs_new_inode(struct sysfs_dirent *sd) * @dentry: dentry to be instantiated * @inode: inode associated with @sd * - * Instantiate @dentry with @inode. + * Unlock @inode if locked and instantiate @dentry with @inode. * * LOCKING: * None. @@ -189,9 +191,13 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) { BUG_ON(!dentry || dentry->d_inode); - 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; + if (inode->i_state & I_NEW) { + unlock_new_inode(inode); + + 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; + } } d_instantiate(dentry, inode); -- cgit v1.2.3 From 0c73f18b7d95de8a007039337063a770b5fc8e7a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:18 +0900 Subject: sysfs: use singly-linked list for sysfs_dirent tree Make sysfs_dirent use singly linked list for its tree structure. sysfs_link_sibling() and sysfs_unlink_sibling() functions are added to handle simpler cases. It adds some complexity and cpu cycle overhead but reduced memory footprint is worthwhile on big machines. This change reduces the sizeof sysfs_dirent from 104 to 88 on 64bit and from 60 to 52 on 32bit. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 3eab9c46a71..732fd7f371e 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -284,8 +284,8 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) int sysfs_hash_and_remove(struct dentry * dir, const char * name) { - struct sysfs_dirent * sd; - struct sysfs_dirent * parent_sd; + struct sysfs_dirent **pos, *sd; + struct sysfs_dirent *parent_sd = dir->d_fsdata; int found = 0; if (!dir) @@ -295,13 +295,15 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) /* no inode means this hasn't been made visible yet */ return -ENOENT; - parent_sd = dir->d_fsdata; mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); - list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) { + sd = *pos; + if (!sd->s_type) continue; if (!strcmp(sd->s_name, name)) { - list_del_init(&sd->s_sibling); + *pos = sd->s_sibling; + sd->s_sibling = NULL; found = 1; break; } -- cgit v1.2.3 From 9d9307dabb3de8140fb3801bf6eb01f231dbd83d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 14 Jun 2007 03:45:18 +0900 Subject: sysfs: Fix oops in sysfs_drop_dentry on x86_64 Fix oops on x86_64 caused by the dereference of dir in sysfs_drop_dentry() made before checking if dir is not NULL (cf. http://marc.info/?l=linux-kernel&m=118151626704924&w=2). Signed-off-by: Rafael J. Wysocki Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 732fd7f371e..ee31bf369a8 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -285,7 +285,7 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) int sysfs_hash_and_remove(struct dentry * dir, const char * name) { struct sysfs_dirent **pos, *sd; - struct sysfs_dirent *parent_sd = dir->d_fsdata; + struct sysfs_dirent *parent_sd; int found = 0; if (!dir) @@ -295,6 +295,7 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) /* no inode means this hasn't been made visible yet */ return -ENOENT; + parent_sd = dir->d_fsdata; mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) { sd = *pos; -- cgit v1.2.3 From d0bcb5689a521df98bff7549fcb8b17499660a99 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:21 +0900 Subject: sysfs: make sysfs_drop_dentry() access inodes using ilookup() sysfs_drop_dentry() used to go through sd->s_dentry and sd->s_parent->s_dentry to access the inodes. This is incorrect because inode can be cached without dentry. This patch makes sysfs_drop_dentry() access inodes using ilookup() on sd->s_ino. This is both correct and simpler. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 63 +++++++++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 35 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index ee31bf369a8..63daa06c419 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -219,9 +219,9 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) */ void sysfs_drop_dentry(struct sysfs_dirent *sd) { - struct dentry *dentry = NULL, *parent = NULL; - struct inode *dir; + struct dentry *dentry = NULL; struct timespec curtime; + struct inode *inode; /* We're not holding a reference to ->s_dentry dentry but the * field will stay valid as long as sysfs_lock is held. @@ -229,19 +229,9 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) spin_lock(&sysfs_lock); spin_lock(&dcache_lock); + /* drop dentry if it's there and dput() didn't kill it yet */ if (sd->s_dentry && sd->s_dentry->d_inode) { - /* get dentry if it's there and dput() didn't kill it yet */ dentry = dget_locked(sd->s_dentry); - parent = dentry->d_parent; - } else if (sd->s_parent->s_dentry->d_inode) { - /* We need to update the parent even if dentry for the - * victim itself doesn't exist. - */ - parent = dget_locked(sd->s_parent->s_dentry); - } - - /* drop */ - if (dentry) { spin_lock(&dentry->d_lock); __d_drop(dentry); spin_unlock(&dentry->d_lock); @@ -250,36 +240,39 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) spin_unlock(&dcache_lock); spin_unlock(&sysfs_lock); - /* nothing to do if the parent isn't in dcache */ - if (!parent) - return; + dput(dentry); + /* XXX: unpin if directory, this will go away soon */ + if (sd->s_type & SYSFS_DIR) + dput(dentry); /* adjust nlink and update timestamp */ - dir = parent->d_inode; - mutex_lock(&dir->i_mutex); - curtime = CURRENT_TIME; - dir->i_ctime = dir->i_mtime = curtime; + inode = ilookup(sysfs_sb, sd->s_ino); + if (inode) { + mutex_lock(&inode->i_mutex); - if (dentry) { - dentry->d_inode->i_ctime = curtime; - drop_nlink(dentry->d_inode); - if (sd->s_type & SYSFS_DIR) { - drop_nlink(dentry->d_inode); - drop_nlink(dir); - /* XXX: unpin if directory, this will go away soon */ - dput(dentry); - } + inode->i_ctime = curtime; + drop_nlink(inode); + if (sd->s_type & SYSFS_DIR) + drop_nlink(inode); + + mutex_unlock(&inode->i_mutex); + iput(inode); } - mutex_unlock(&dir->i_mutex); + /* adjust nlink and udpate timestamp of the parent */ + inode = ilookup(sysfs_sb, sd->s_parent->s_ino); + if (inode) { + mutex_lock(&inode->i_mutex); - /* bye bye */ - if (dentry) - dput(dentry); - else - dput(parent); + inode->i_ctime = inode->i_mtime = curtime; + if (sd->s_type & SYSFS_DIR) + drop_nlink(inode); + + mutex_unlock(&inode->i_mutex); + iput(inode); + } } int sysfs_hash_and_remove(struct dentry * dir, const char * name) -- cgit v1.2.3 From b402d72cf7b338a074e3c12b305ec79284e18845 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:21 +0900 Subject: sysfs: rename sysfs_dirent->s_type to s_flags and make room for flags Rename sysfs_dirent->s_type to s_flags, pack type into lower eight bits and reserve the rest for flags. sysfs_type() can used to access the type. All existing sd->s_type accesses are converted to use sysfs_type(). While at it, type test is changed to equality test instead of bit-and test where appropriate. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 63daa06c419..ee3a5d95705 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -242,7 +242,7 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) dput(dentry); /* XXX: unpin if directory, this will go away soon */ - if (sd->s_type & SYSFS_DIR) + if (sysfs_type(sd) == SYSFS_DIR) dput(dentry); /* adjust nlink and update timestamp */ @@ -254,7 +254,7 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) inode->i_ctime = curtime; drop_nlink(inode); - if (sd->s_type & SYSFS_DIR) + if (sysfs_type(sd) == SYSFS_DIR) drop_nlink(inode); mutex_unlock(&inode->i_mutex); @@ -267,7 +267,7 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) mutex_lock(&inode->i_mutex); inode->i_ctime = inode->i_mtime = curtime; - if (sd->s_type & SYSFS_DIR) + if (sysfs_type(sd) == SYSFS_DIR) drop_nlink(inode); mutex_unlock(&inode->i_mutex); @@ -293,7 +293,7 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) { sd = *pos; - if (!sd->s_type) + if (!sysfs_type(sd)) continue; if (!strcmp(sd->s_name, name)) { *pos = sd->s_sibling; -- cgit v1.2.3 From 380e6fbb729a55b73d5d8409551474884e0d93fc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:22 +0900 Subject: sysfs: implement SYSFS_FLAG_REMOVED flag Implement SYSFS_FLAG_REMOVED flag which currently is used only to improve sanity check in sysfs_deactivate(). The flag will be used to make directory entries reclamiable. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index ee3a5d95705..e2f6ef138d2 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -296,6 +296,7 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) if (!sysfs_type(sd)) continue; if (!strcmp(sd->s_name, name)) { + sd->s_flags |= SYSFS_FLAG_REMOVED; *pos = sd->s_sibling; sd->s_sibling = NULL; found = 1; -- cgit v1.2.3 From 608e266a2d4e62c1b98c1c573064b6afe8c06a58 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:22 +0900 Subject: sysfs: make kobj point to sysfs_dirent instead of dentry As kobj sysfs dentries and inodes are gonna be made reclaimable, dentry can't be used as naming token for sysfs file/directory, replace kobj->dentry with kobj->sd. The only external interface change is shadow directory handling. All other changes are contained in kobj and sysfs. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index e2f6ef138d2..1be853706e9 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -275,22 +275,23 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) } } -int sysfs_hash_and_remove(struct dentry * dir, const char * name) +int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) { + struct dentry *dir; struct sysfs_dirent **pos, *sd; - struct sysfs_dirent *parent_sd; int found = 0; - if (!dir) + if (!dir_sd) return -ENOENT; + dir = dir_sd->s_dentry; + if (dir->d_inode == NULL) /* no inode means this hasn't been made visible yet */ return -ENOENT; - parent_sd = dir->d_fsdata; mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); - for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) { + for (pos = &dir_sd->s_children; *pos; pos = &(*pos)->s_sibling) { sd = *pos; if (!sysfs_type(sd)) -- cgit v1.2.3 From 5f9953237f684ea1778adb9d26162da00b282225 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:23 +0900 Subject: sysfs: consolidate sysfs spinlocks Replace sysfs_lock and kobj_sysfs_assoc_lock with sysfs_assoc_lock. sysfs_lock was originally to be used to protect sysfs_dirent tree but mutex seems better choice, so there is no reason to keep sysfs_lock separate. Merge the two spinlocks into one. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 1be853706e9..e4c23939fb3 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -211,11 +211,11 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) * parent on entry to this function such that it can't be looked * up anymore. * - * @sd->s_dentry which is protected with sysfs_lock points to the - * currently associated dentry but we're not holding a reference - * to it and racing with dput(). Grab dcache_lock and verify - * dentry before dropping it. If @sd->s_dentry is NULL or dput() - * beats us, no need to bother. + * @sd->s_dentry which is protected with sysfs_assoc_lock points + * to the currently associated dentry but we're not holding a + * reference to it and racing with dput(). Grab dcache_lock and + * verify dentry before dropping it. If @sd->s_dentry is NULL or + * dput() beats us, no need to bother. */ void sysfs_drop_dentry(struct sysfs_dirent *sd) { @@ -224,9 +224,9 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) struct inode *inode; /* We're not holding a reference to ->s_dentry dentry but the - * field will stay valid as long as sysfs_lock is held. + * field will stay valid as long as sysfs_assoc_lock is held. */ - spin_lock(&sysfs_lock); + spin_lock(&sysfs_assoc_lock); spin_lock(&dcache_lock); /* drop dentry if it's there and dput() didn't kill it yet */ @@ -238,7 +238,7 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) } spin_unlock(&dcache_lock); - spin_unlock(&sysfs_lock); + spin_unlock(&sysfs_assoc_lock); dput(dentry); /* XXX: unpin if directory, this will go away soon */ -- cgit v1.2.3 From 3007e997de91ec59af39a3f9c91595b31ae6e08b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:23 +0900 Subject: sysfs: use sysfs_mutex to protect the sysfs_dirent tree As kobj sysfs dentries and inodes are gonna be made reclaimable, i_mutex can't be used to protect sysfs_dirent tree. Use sysfs_mutex globally instead. As the whole tree is protected with sysfs_mutex, there is no reason to keep sysfs_rename_sem. Drop it. While at it, add docbook comments to functions which require sysfs_mutex locking. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index e4c23939fb3..d439c0b4bfc 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -277,20 +277,14 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) { - struct dentry *dir; struct sysfs_dirent **pos, *sd; int found = 0; if (!dir_sd) return -ENOENT; - dir = dir_sd->s_dentry; + mutex_lock(&sysfs_mutex); - if (dir->d_inode == NULL) - /* no inode means this hasn't been made visible yet */ - return -ENOENT; - - mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); for (pos = &dir_sd->s_children; *pos; pos = &(*pos)->s_sibling) { sd = *pos; @@ -304,7 +298,8 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) break; } } - mutex_unlock(&dir->d_inode->i_mutex); + + mutex_unlock(&sysfs_mutex); if (!found) return -ENOENT; -- cgit v1.2.3 From fb6896da37f19be4b75154c14d1cd79231255b17 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:24 +0900 Subject: sysfs: restructure add/remove paths and fix inode update The original add/remove code had the following problems. * parent's timestamps are updated on dentry instantiation. this is incorrect with reclaimable files. * updating parent's timestamps isn't synchronized. * parent nlink update assumes the inode is accessible which won't be true once directory dentries are made reclaimable. This patch restructures add/remove paths to resolve the above problems. Add/removal are done in the following steps. 1. sysfs_addrm_start() : acquire locks including sysfs_mutex and other resources. 2-a. sysfs_add_one() : add new sd. linking the new sd into the children list is caller's responsibility. 2-b. sysfs_remove_one() : remove a sd. unlinking the sd from the children list is caller's responsibility. 3. sysfs_addrm_finish() : release all resources and clean up. Steps 2-a and/or 2-b can be repeated multiple times. Parent's inode is looked up during sysfs_addrm_start(). If available (always at the moment), it's pinned and nlink is updated as sd's are added and removed. Timestamps are updated during finish if any sd has been added or removed. If parent's inode is not available during start, sysfs_mutex ensures that parent inode is not created till add/remove is complete. All the complexity is contained inside the helper functions. Especially, dentry/inode handling is properly hidden from the rest of sysfs which now mostly operate on sysfs_dirents. As an added bonus, codes which use these helpers to add and remove sysfs_dirents are now more structured and simpler. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 46 ++++++++-------------------------------------- 1 file changed, 8 insertions(+), 38 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index d439c0b4bfc..f95966847a8 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -191,15 +191,9 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) { BUG_ON(!dentry || dentry->d_inode); - if (inode->i_state & I_NEW) { + if (inode->i_state & I_NEW) unlock_new_inode(inode); - 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; - } - } - d_instantiate(dentry, inode); } @@ -220,7 +214,6 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) void sysfs_drop_dentry(struct sysfs_dirent *sd) { struct dentry *dentry = NULL; - struct timespec curtime; struct inode *inode; /* We're not holding a reference to ->s_dentry dentry but the @@ -246,13 +239,11 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) dput(dentry); /* adjust nlink and update timestamp */ - curtime = CURRENT_TIME; - inode = ilookup(sysfs_sb, sd->s_ino); if (inode) { mutex_lock(&inode->i_mutex); - inode->i_ctime = curtime; + inode->i_ctime = CURRENT_TIME; drop_nlink(inode); if (sysfs_type(sd) == SYSFS_DIR) drop_nlink(inode); @@ -260,30 +251,17 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) mutex_unlock(&inode->i_mutex); iput(inode); } - - /* adjust nlink and udpate timestamp of the parent */ - inode = ilookup(sysfs_sb, sd->s_parent->s_ino); - if (inode) { - mutex_lock(&inode->i_mutex); - - inode->i_ctime = inode->i_mtime = curtime; - if (sysfs_type(sd) == SYSFS_DIR) - drop_nlink(inode); - - mutex_unlock(&inode->i_mutex); - iput(inode); - } } int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) { + struct sysfs_addrm_cxt acxt; struct sysfs_dirent **pos, *sd; - int found = 0; if (!dir_sd) return -ENOENT; - mutex_lock(&sysfs_mutex); + sysfs_addrm_start(&acxt, dir_sd); for (pos = &dir_sd->s_children; *pos; pos = &(*pos)->s_sibling) { sd = *pos; @@ -291,22 +269,14 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) if (!sysfs_type(sd)) continue; if (!strcmp(sd->s_name, name)) { - sd->s_flags |= SYSFS_FLAG_REMOVED; *pos = sd->s_sibling; sd->s_sibling = NULL; - found = 1; + sysfs_remove_one(&acxt, sd); break; } } - mutex_unlock(&sysfs_mutex); - - if (!found) - return -ENOENT; - - sysfs_drop_dentry(sd); - sysfs_deactivate(sd); - sysfs_put(sd); - - return 0; + if (sysfs_addrm_finish(&acxt)) + return 0; + return -ENOENT; } -- cgit v1.2.3 From a0edd7c848945a75e2f41673f43bc37d0a5fed15 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:24 +0900 Subject: sysfs: move sysfs_drop_dentry() to dir.c and make it static After add/remove path restructuring, the only user of sysfs_drop_dentry() is sysfs_addrm_finish(). Move sysfs_drop_dentry() to dir.c and make it static. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 56 -------------------------------------------------------- 1 file changed, 56 deletions(-) (limited to 'fs/sysfs/inode.c') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index f95966847a8..3756e152285 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -197,62 +197,6 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) d_instantiate(dentry, inode); } -/** - * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent - * @sd: target sysfs_dirent - * - * Drop dentry for @sd. @sd must have been unlinked from its - * parent on entry to this function such that it can't be looked - * up anymore. - * - * @sd->s_dentry which is protected with sysfs_assoc_lock points - * to the currently associated dentry but we're not holding a - * reference to it and racing with dput(). Grab dcache_lock and - * verify dentry before dropping it. If @sd->s_dentry is NULL or - * dput() beats us, no need to bother. - */ -void sysfs_drop_dentry(struct sysfs_dirent *sd) -{ - struct dentry *dentry = NULL; - struct inode *inode; - - /* We're not holding a reference to ->s_dentry dentry but the - * field will stay valid as long as sysfs_assoc_lock is held. - */ - spin_lock(&sysfs_assoc_lock); - spin_lock(&dcache_lock); - - /* drop dentry if it's there and dput() didn't kill it yet */ - if (sd->s_dentry && sd->s_dentry->d_inode) { - dentry = dget_locked(sd->s_dentry); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); - } - - spin_unlock(&dcache_lock); - spin_unlock(&sysfs_assoc_lock); - - dput(dentry); - /* XXX: unpin if directory, this will go away soon */ - if (sysfs_type(sd) == SYSFS_DIR) - dput(dentry); - - /* adjust nlink and update timestamp */ - inode = ilookup(sysfs_sb, sd->s_ino); - if (inode) { - mutex_lock(&inode->i_mutex); - - inode->i_ctime = CURRENT_TIME; - drop_nlink(inode); - if (sysfs_type(sd) == SYSFS_DIR) - drop_nlink(inode); - - mutex_unlock(&inode->i_mutex); - iput(inode); - } -} - int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) { struct sysfs_addrm_cxt acxt; -- cgit v1.2.3