From: Tejun Heo <htejun@gmail.com>
To: maneesh@in.ibm.com
Cc: Greg KH <greg@kroah.com>,
Andrew Morton <akpm@linux-foundation.org>,
Clemens Schwaighofer <cs@tequila.co.jp>,
linux-kernel <linux-kernel@vger.kernel.org>,
Dipankar Sarma <dipankar@in.ibm.com>,
Chuck Ebbert <cebbert@redhat.com>
Subject: [PATCH 2/2] sysfs: fix race condition around sd->s_dentry
Date: Thu, 17 May 2007 19:59:29 +0200 [thread overview]
Message-ID: <464C9801.8000606@gmail.com> (raw)
In-Reply-To: <464C95AB.3020209@gmail.com>
Allowing attribute and symlink dentries to be reclaimed means
sd->s_dentry can change dynamically. However, updates to the field
are unsynchronized leading to race conditions. This patch adds
sysfs_lock and use it to synchronize updates to sd->s_dentry.
Due to the locking around ->d_iput, the check in sysfs_drop_dentry()
is complex. sysfs_lock only protect sd->s_dentry pointer itself. The
validity of the dentry is protected by dcache_lock, so whether dentry
is alive or not can only be tested while holding both locks.
This is minimal backport of sysfs_drop_dentry() rewrite in devel
branch.
DONT APPLY JUST YET
---
Okay, here's minimal backport of synchronization fix. kobj
directories are left alone. sysfs_drop_dentry() modifications can be
made more efficient but are left that way on purpose.
fs/sysfs/dir.c | 29 ++++++++++++++++++++++++++---
fs/sysfs/inode.c | 28 ++++++++++++++++++++++++----
fs/sysfs/sysfs.h | 1 +
3 files changed, 51 insertions(+), 7 deletions(-)
Index: work/fs/sysfs/dir.c
===================================================================
--- work.orig/fs/sysfs/dir.c
+++ work/fs/sysfs/dir.c
@@ -13,14 +13,26 @@
#include "sysfs.h"
DECLARE_RWSEM(sysfs_rename_sem);
+spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED;
static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
{
struct sysfs_dirent * sd = dentry->d_fsdata;
if (sd) {
- BUG_ON(sd->s_dentry != dentry);
- sd->s_dentry = NULL;
+ /* sd->s_dentry is protected with sysfs_lock. This
+ * allows sysfs_drop_dentry() to dereference it.
+ */
+ spin_lock(&sysfs_lock);
+
+ /* The dentry might have been deleted or another
+ * lookup could have happened updating sd->s_dentry to
+ * point the new dentry. Ignore if it isn't pointing
+ * to this dentry.
+ */
+ if (sd->s_dentry == dentry)
+ sd->s_dentry = NULL;
+ spin_unlock(&sysfs_lock);
sysfs_put(sd);
}
iput(inode);
@@ -238,7 +250,10 @@ static int sysfs_attach_attr(struct sysf
}
dentry->d_fsdata = sysfs_get(sd);
+ /* protect sd->s_dentry against sysfs_d_iput */
+ spin_lock(&sysfs_lock);
sd->s_dentry = dentry;
+ spin_unlock(&sysfs_lock);
error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init);
if (error) {
sysfs_put(sd);
@@ -260,7 +275,10 @@ static int sysfs_attach_link(struct sysf
int err = 0;
dentry->d_fsdata = sysfs_get(sd);
+ /* protect sd->s_dentry against sysfs_d_iput */
+ spin_lock(&sysfs_lock);
sd->s_dentry = dentry;
+ spin_unlock(&sysfs_lock);
err = sysfs_create(dentry, S_IFLNK|S_IRWXUGO, init_symlink);
if (!err) {
dentry->d_op = &sysfs_dentry_ops;
@@ -329,6 +347,7 @@ void sysfs_remove_subdir(struct dentry *
static void __sysfs_remove_dir(struct dentry *dentry)
{
+ LIST_HEAD(removed);
struct sysfs_dirent * parent_sd;
struct sysfs_dirent * sd, * tmp;
@@ -342,11 +361,15 @@ static void __sysfs_remove_dir(struct de
list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
if (!sd->s_element || !(sd->s_type & SYSFS_NOT_PINNED))
continue;
+ list_move(&sd->s_sibling, &removed);
+ }
+ mutex_unlock(&dentry->d_inode->i_mutex);
+
+ list_for_each_entry_safe(sd, tmp, &removed, s_sibling) {
list_del_init(&sd->s_sibling);
sysfs_drop_dentry(sd, dentry);
sysfs_put(sd);
}
- mutex_unlock(&dentry->d_inode->i_mutex);
remove_dir(dentry);
/**
Index: work/fs/sysfs/inode.c
===================================================================
--- work.orig/fs/sysfs/inode.c
+++ work/fs/sysfs/inode.c
@@ -244,9 +244,23 @@ static inline void orphan_all_buffers(st
*/
void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
{
- struct dentry * dentry = sd->s_dentry;
+ 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.
+ */
+ spin_lock(&sysfs_lock);
+ spin_lock(&dcache_lock);
+
+ /* dget dentry if it's still alive */
+ if (sd->s_dentry && sd->s_dentry->d_inode)
+ dentry = dget_locked(sd->s_dentry);
+
+ spin_unlock(&dcache_lock);
+ spin_unlock(&sysfs_lock);
+
+ /* drop dentry */
if (dentry) {
spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
@@ -266,6 +280,8 @@ void sysfs_drop_dentry(struct sysfs_dire
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
}
+
+ dput(dentry);
}
}
@@ -289,13 +305,17 @@ int sysfs_hash_and_remove(struct dentry
continue;
if (!strcmp(sysfs_get_name(sd), 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_drop_dentry(sd, dir);
+ sysfs_put(sd);
+
+ return 0;
}
Index: work/fs/sysfs/sysfs.h
===================================================================
--- work.orig/fs/sysfs/sysfs.h
+++ work/fs/sysfs/sysfs.h
@@ -32,6 +32,7 @@ extern const unsigned char * sysfs_get_n
extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent);
extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
+extern spinlock_t sysfs_lock;
extern struct rw_semaphore sysfs_rename_sem;
extern struct super_block * sysfs_sb;
extern const struct file_operations sysfs_dir_operations;
next prev parent reply other threads:[~2007-05-17 17:59 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-05-16 0:24 Oops and Panics in 2.6.21.1, 2.6.20.6 and 2.6.19.2 Clemens Schwaighofer
2007-05-16 1:52 ` Clemens Schwaighofer
2007-05-16 1:53 ` Andrew Morton
2007-05-16 2:02 ` Clemens Schwaighofer
2007-05-16 2:46 ` Clemens Schwaighofer
2007-05-16 3:18 ` Andrew Morton
2007-05-16 11:05 ` Tejun Heo
2007-05-16 15:29 ` Andrew Morton
2007-05-16 15:40 ` Tejun Heo
2007-05-16 16:06 ` Chuck Ebbert
2007-05-16 16:13 ` Andrew Morton
2007-05-16 18:31 ` [PATCH -stable] sysfs: disable reclamation by default Tejun Heo
2007-05-17 12:04 ` Greg KH
2007-05-17 17:39 ` Maneesh Soni
2007-05-17 17:49 ` Tejun Heo
2007-05-17 17:52 ` [PATCH 1/2] sysfs: fix condition check in sysfs_drop_dentry() Tejun Heo
2007-05-21 4:35 ` Maneesh Soni
2007-05-17 17:59 ` Tejun Heo [this message]
2007-05-17 18:16 ` [PATCH 2/2] sysfs: fix race condition around sd->s_dentry, take#2 Tejun Heo
2007-05-21 5:01 ` Maneesh Soni
2007-05-21 16:02 ` Eric Sandeen
2007-05-21 16:15 ` Tejun Heo
2007-05-22 22:38 ` Greg KH
2007-05-23 8:21 ` Tejun Heo
2007-06-08 14:35 ` Tejun Heo
2007-06-09 6:49 ` Tejun Heo
2007-06-10 16:18 ` Greg KH
2007-05-21 4:39 ` [PATCH -stable] sysfs: disable reclamation by default Maneesh Soni
2007-05-17 18:54 ` Oops and Panics in 2.6.21.1, 2.6.20.6 and 2.6.19.2 Eric Sandeen
2007-06-29 2:51 ` Clemens Schwaighofer
2007-06-29 6:12 ` Satyam Sharma
2007-06-29 6:18 ` Clemens Schwaighofer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=464C9801.8000606@gmail.com \
--to=htejun@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=cebbert@redhat.com \
--cc=cs@tequila.co.jp \
--cc=dipankar@in.ibm.com \
--cc=greg@kroah.com \
--cc=linux-kernel@vger.kernel.org \
--cc=maneesh@in.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.