From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1764214AbXFKFEW (ORCPT ); Mon, 11 Jun 2007 01:04:22 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1751286AbXFKFEO (ORCPT ); Mon, 11 Jun 2007 01:04:14 -0400 Received: from wa-out-1112.google.com ([209.85.146.182]:2494 "EHLO wa-out-1112.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751863AbXFKFEM (ORCPT ); Mon, 11 Jun 2007 01:04:12 -0400 DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:date:from:to:subject:message-id:references:mime-version:content-type:content-disposition:in-reply-to:user-agent; b=qme+UKDffnEz6R/Ta/MGE4kpNkatWkvZ+dsLVCwUWi3kv1bNta+O/g5eMYrQCbuEgxO4j+6oP+cAZNCk8Gy+Q3Mh+N9BQ0MEwuGojoY1PL11dfeRi5mtRKLek/B9OLejGAihicAp2Ji5X58x+AHmuaRLfurbZzXlIQt3GLG14KI= Date: Mon, 11 Jun 2007 14:04:01 +0900 From: Tejun Heo To: linux-kernel@vger.kernel.org, greg@kroah.com, akpm@linux-foundation.org, cebbert@redhat.com, sandeen@redhat.com, maneesh@in.ibm.com, cs@tequila.co.jp Subject: [PATCH 3/3] sysfs: fix race condition around sd->s_dentry, take#2 Message-ID: <20070611050401.GM29122@htj.dyndns.org> References: <20070611050107.GJ29122@htj.dyndns.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20070611050107.GJ29122@htj.dyndns.org> User-Agent: Mutt/1.5.13 (2006-08-11) Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org 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. Signed-off-by: Tejun Heo --- fs/sysfs/dir.c | 22 ++++++++++++++++++++-- fs/sysfs/inode.c | 18 +++++++++++++++++- fs/sysfs/sysfs.h | 1 + 3 files changed, 38 insertions(+), 3 deletions(-) Index: work1/fs/sysfs/dir.c =================================================================== --- work1.orig/fs/sysfs/dir.c +++ work1/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); @@ -247,7 +259,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); @@ -269,7 +284,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; Index: work1/fs/sysfs/inode.c =================================================================== --- work1.orig/fs/sysfs/inode.c +++ work1/fs/sysfs/inode.c @@ -246,9 +246,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); @@ -268,6 +282,8 @@ void sysfs_drop_dentry(struct sysfs_dire spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); } + + dput(dentry); } } Index: work1/fs/sysfs/sysfs.h =================================================================== --- work1.orig/fs/sysfs/sysfs.h +++ work1/fs/sysfs/sysfs.h @@ -33,6 +33,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;