From nobody Tue Apr 7 16:16:41 2026 Received: from flow-b6-smtp.messagingengine.com (flow-b6-smtp.messagingengine.com [202.12.124.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 425E534A797; Thu, 12 Mar 2026 21:58:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=202.12.124.141 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773352713; cv=none; b=Xih5iItiq46g5beVbKcRdndH58wA7zPNUJiKZNrJmKruVyPn2U6lC47Q7r0Q46cUrxhs9A/OGuPej7419nWkDYz+nRflLAItEOlvj9vKU/swxAkCaLz8Dp1tBXiF4uZv5WKuSbYGCpqIYfcPJ2RvUEKh2Byy60GjRdgLxm/4rB4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773352713; c=relaxed/simple; bh=1JkkAedomWBG14G0WAqvfZiAthqy/JF/uu16ADzus+Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nKClF/lPsAmEGh9OIqDY4oHVLGofE6kPQDWaZ5j3aTXQQwk28Zo9Ejc+j4eIgjgnGOBa2fnjYVSfU2gikDI2wzK1FZwRcFk7II7mZvlDUTpPNtWkE/JdRkWnDLrUEenxZo2JmTB99Uu0tbz7cvkvxnKGvxYKj4ycRe+sY2AGsuA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ownmail.net; spf=pass smtp.mailfrom=ownmail.net; dkim=pass (2048-bit key) header.d=ownmail.net header.i=@ownmail.net header.b=WfHfWNbc; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=Qx5rCR2g; arc=none smtp.client-ip=202.12.124.141 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ownmail.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ownmail.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=ownmail.net header.i=@ownmail.net header.b="WfHfWNbc"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="Qx5rCR2g" Received: from phl-compute-02.internal (phl-compute-02.internal [10.202.2.42]) by mailflow.stl.internal (Postfix) with ESMTP id 6F3A01301B63; Thu, 12 Mar 2026 17:58:29 -0400 (EDT) Received: from phl-frontend-03 ([10.202.2.162]) by phl-compute-02.internal (MEProxy); Thu, 12 Mar 2026 17:58:30 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ownmail.net; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to; s=fm1; t=1773352709; x=1773359909; bh=1h/4KUNGDnlL7CBwVpO5v6kJjyfz+9NwtNaAhRC9U10=; b= WfHfWNbch9UGB+eGabQxTdEvIDD4DFelol7k3LjdQO/lb65xfuejEmzAoM8pvQ4+ gJ4ol9X9HCBFTIzeKB0Viue1L3jG1Q9bqBwZUTphx4m6U3Rk2ls8GfzEdN0PiZi5 LCTknHndu4dqomidg/k3sGUcGTijbG2dTWjKHVNypIRljDAxdhO3oxfrhMJkCel/ D96BUDM2dcr86sPQiHqSYInXl70rtCkrmk8TxgB7ddCaXEgipNdaZIjXJPrb/0fJ BpP944NRphZdvubke4smP5FoL2inwaqZC6E92n9ErQLi+cdxDBeLIX0EUFb3yR9T sC3KzRrjSgOF44kCJiHBIA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm1; t=1773352709; x=1773359909; bh=1 h/4KUNGDnlL7CBwVpO5v6kJjyfz+9NwtNaAhRC9U10=; b=Qx5rCR2gTxh9pg1aW AjDwaxfHDw0YiqmAUCgdlStHI0PQga3+vcnB8gfVa/y4ZBpWzH0QrLbujnOmZKv8 6tBLD2DKb69VB5WgrYmfGt41UvEtR9qwufkNY6SmUVXS1uuj8MG8L6/WcUbufqDc tGCfJnZa7u52el6AobUREmziO6p1gxAhhfQlvnqIsQf2Ry0YMLdAYgehYR7OvjBg Hf7z6Fv/FTfS3TEpRoly9oXmw9v58aAJTrvP/BMYbLSbx0Zd0jSOWF8jkC76ErM+ pvhKfqXWeCDyRvi7kvD8qfxdIcxMmWn10jVuo8IUlE8Mq9uzo+hkJ6Se7a+KyFPv Jtq8g== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefgedrtddtgddvkeejledvucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhhrggfgsedtkeertdertddtnecuhfhrohhmpefpvghilheu rhhofihnuceonhgvihhlsgesohifnhhmrghilhdrnhgvtheqnecuggftrfgrthhtvghrnh epveevkeffudeuvefhieeghffgudektdelkeejiedtjedugfeukedvkeffvdefvddunecu vehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepnhgvihhlsg esohifnhhmrghilhdrnhgvthdpnhgspghrtghpthhtohephedupdhmohguvgepshhmthhp ohhuthdprhgtphhtthhopehvihhrohesiigvnhhivhdrlhhinhhugidrohhrghdruhhkpd hrtghpthhtoheplhhinhhugidqgihfshesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgt phhtthhopehlihhnuhigqdhunhhiohhnfhhssehvghgvrhdrkhgvrhhnvghlrdhorhhgpd hrtghpthhtoheplhhinhhugidqthhrrggtvgdqkhgvrhhnvghlsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqnhhfshesvhhgvghrrdhkvghrnhgvlh drohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgv lhdrohhrghdprhgtphhtthhopehlihhnuhigqdhfshguvghvvghlsehvghgvrhdrkhgvrh hnvghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqvgigthegsehvghgvrhdrkhgvrhhn vghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqvghfihesvhhgvghrrdhkvghrnhgvlh drohhrgh X-ME-Proxy: Feedback-ID: i9d664b8f:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 12 Mar 2026 17:58:15 -0400 (EDT) From: NeilBrown To: Linus Torvalds , Alexander Viro , Christian Brauner , Jan Kara , Jeff Layton , Trond Myklebust , Anna Schumaker , Carlos Maiolino , Miklos Szeredi , Amir Goldstein , Jan Harkes , Hugh Dickins , Baolin Wang , David Howells , Marc Dionne , Steve French , Namjae Jeon , Sungjong Seo , Yuezhang Mo , Andreas Hindborg , Breno Leitao , "Theodore Ts'o" , Andreas Dilger , Steven Rostedt , Masami Hiramatsu , Ilya Dryomov , Alex Markuze , Viacheslav Dubeyko , Tyler Hicks , Andreas Gruenbacher , Richard Weinberger , Anton Ivanov , Johannes Berg , Jeremy Kerr , Ard Biesheuvel Cc: linux-fsdevel@vger.kernel.org, linux-nfs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-unionfs@vger.kernel.org, coda@cs.cmu.edu, linux-mm@kvack.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, ceph-devel@vger.kernel.org, ecryptfs@vger.kernel.org, gfs2@lists.linux.dev, linux-um@lists.infradead.org, linux-efi@vger.kernel.org Subject: [PATCH 52/53] VFS: lift d_alloc_parallel above inode_lock Date: Fri, 13 Mar 2026 08:12:39 +1100 Message-ID: <20260312214330.3885211-53-neilb@ownmail.net> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20260312214330.3885211-1-neilb@ownmail.net> References: <20260312214330.3885211-1-neilb@ownmail.net> Reply-To: NeilBrown Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: NeilBrown d_alloc_parallel() can block waiting on a d_in_lookup() dentry so it is important to order it consistently with other blocking locks such as inode_lock(). Currenty d_alloc_parallel() is ordered after inode_lock(): it can be called while the inode is locked, and so the inode cannot be locked while a d_in_lookup() dentry is held. This patch reverses that order. d_alloc_parallel() must now be called *before* locking the directory, and must not be called afterwards. This allows directory locking to be moved closer to the filesystem operations, and ultimately into those operations. lookup_one_qstr_excl() is now called without an lock held, exclusive or otherwise, so the "_excl" is dropped - it is now lookup_one_qstr(). As a lock is taken *after* lookup, start_dirop() and start_renaming() must ensure that if the dentry isn't d_in_lookup() that after the lock is taken the parent is still correct and the dentry is still hashed. lookup_one_qstr() and lookup_slow() don't need to re-check the parent as the dentry is always d_in_lookup() so parent cannot change. The locking in lookup_slow() is moved into __lookup_slow() immediately before/after ->lookup, and lookup_slow() just sets the task state for waiting. Parent locking is removed from open_last_lookups() and performed in lookup_open(). A shared lock is taken if ->lookup() needs to be called. An exclusive lock is taken separately if ->create() needs to be called - with checks that the dentry hasn't become positive. If ->atomic_open is needed we take exclusive or shared parent lock as appropriate and check for a positive dentry or DEAD parent. The fsnotify_create() call is kept inside the locked region in lookup_open(). I don't know if this is important. Signed-off-by: NeilBrown --- fs/namei.c | 239 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 154 insertions(+), 85 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index bba419f2fc53..3d213070a515 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1773,8 +1773,19 @@ static struct dentry *lookup_dcache(const struct qst= r *name, return dentry; } =20 +static inline bool inode_lock_shared_state(struct inode *inode, unsigned i= nt state) +{ + if (state =3D=3D TASK_KILLABLE) { + if (down_read_killable(&inode->i_rwsem) !=3D 0) { + return false; + } + } else { + inode_lock_shared(inode); + } + return true; +} + /* - * Parent directory has inode locked. * If Lookup_EXCL or LOOKUP_RENAME_TARGET is set * d_lookup_done() must be called before the dentry is dput() * If the dentry is not d_in_lookup(): @@ -1783,8 +1794,9 @@ static struct dentry *lookup_dcache(const struct qstr= *name, * If it is d_in_lookup() then these conditions can only be checked by the * file system when carrying out the intent (create or rename). */ -static struct dentry *lookup_one_qstr_excl(const struct qstr *name, - struct dentry *base, unsigned int flags) +static struct dentry *lookup_one_qstr(const struct qstr *name, + struct dentry *base, unsigned int flags, + unsigned int state) { struct dentry *dentry; struct dentry *old; @@ -1806,7 +1818,16 @@ static struct dentry *lookup_one_qstr_excl(const str= uct qstr *name, /* Raced with another thread which did the lookup */ goto found; =20 - old =3D dir->i_op->lookup(dir, dentry, flags); + if (!inode_lock_shared_state(dir, state)) { + d_lookup_done(dentry); + dput(dentry); + return ERR_PTR(-EINTR); + } + if (unlikely(IS_DEADDIR(dir))) + old =3D ERR_PTR(-ENOENT); + else + old =3D dir->i_op->lookup(dir, dentry, flags | LOOKUP_SHARED); + inode_unlock_shared(dir); if (unlikely(old)) { d_lookup_done(dentry); dput(dentry); @@ -1897,7 +1918,8 @@ static struct dentry *lookup_fast(struct nameidata *n= d) /* Fast lookup failed, do it the slow way */ static struct dentry *__lookup_slow(const struct qstr *name, struct dentry *dir, - unsigned int flags) + unsigned int flags, + unsigned int state) { struct dentry *dentry, *old; struct inode *inode =3D dir->d_inode; @@ -1920,8 +1942,17 @@ static struct dentry *__lookup_slow(const struct qst= r *name, dput(dentry); dentry =3D ERR_PTR(error); } + } else if (!inode_lock_shared_state(inode, state)) { + d_lookup_done(dentry); + dput(dentry); + return ERR_PTR(-EINTR); } else { - old =3D inode->i_op->lookup(inode, dentry, flags); + if (unlikely(IS_DEADDIR(inode))) + old =3D ERR_PTR(-ENOENT); + else + old =3D inode->i_op->lookup(inode, dentry, + flags | LOOKUP_SHARED); + inode_unlock_shared(inode); d_lookup_done(dentry); if (unlikely(old)) { dput(dentry); @@ -1935,26 +1966,14 @@ static noinline struct dentry *lookup_slow(const st= ruct qstr *name, struct dentry *dir, unsigned int flags) { - struct inode *inode =3D dir->d_inode; - struct dentry *res; - inode_lock_shared(inode); - res =3D __lookup_slow(name, dir, flags | LOOKUP_SHARED); - inode_unlock_shared(inode); - return res; + return __lookup_slow(name, dir, flags | LOOKUP_SHARED, TASK_NORMAL); } =20 static struct dentry *lookup_slow_killable(const struct qstr *name, struct dentry *dir, unsigned int flags) { - struct inode *inode =3D dir->d_inode; - struct dentry *res; - - if (inode_lock_shared_killable(inode)) - return ERR_PTR(-EINTR); - res =3D __lookup_slow(name, dir, flags | LOOKUP_SHARED); - inode_unlock_shared(inode); - return res; + return __lookup_slow(name, dir, flags | LOOKUP_SHARED, TASK_KILLABLE); } =20 static inline int may_lookup(struct mnt_idmap *idmap, @@ -2908,18 +2927,26 @@ static struct dentry *__start_dirop(struct dentry *= parent, struct qstr *name, struct dentry *dentry; struct inode *dir =3D d_inode(parent); =20 - if (state =3D=3D TASK_KILLABLE) { - int ret =3D down_write_killable_nested(&dir->i_rwsem, - I_MUTEX_PARENT); - if (ret) - return ERR_PTR(ret); - } else { - inode_lock_nested(dir, I_MUTEX_PARENT); - } - dentry =3D lookup_one_qstr_excl(name, parent, lookup_flags); - if (IS_ERR(dentry)) + while(1) { + dentry =3D lookup_one_qstr(name, parent, lookup_flags, state); + if (IS_ERR(dentry)) + return dentry; + if (state =3D=3D TASK_KILLABLE) { + if (down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT) !=3D 0) { + d_lookup_done(dentry); + dput(dentry); + return ERR_PTR(-EINTR); + } + } else { + inode_lock_nested(dir, I_MUTEX_PARENT); + } + if (d_in_lookup(dentry) || + (!d_unhashed(dentry) && dentry->d_parent =3D=3D parent)) + return dentry; inode_unlock(dir); - return dentry; + d_lookup_done(dentry); + dput(dentry); + } } =20 /** @@ -3830,26 +3857,37 @@ __start_renaming(struct renamedata *rd, int lookup_= flags, if (rd->flags & RENAME_NOREPLACE) target_flags |=3D LOOKUP_EXCL; =20 - trap =3D lock_rename(rd->old_parent, rd->new_parent); - if (IS_ERR(trap)) - return PTR_ERR(trap); - - d1 =3D lookup_one_qstr_excl(old_last, rd->old_parent, - lookup_flags); +retry: + d1 =3D lookup_one_qstr(old_last, rd->old_parent, + lookup_flags, TASK_NORMAL); err =3D PTR_ERR(d1); if (IS_ERR(d1)) - goto out_unlock; + goto out_err; =20 - d2 =3D lookup_one_qstr_excl(new_last, rd->new_parent, - lookup_flags | target_flags); + d2 =3D lookup_one_qstr(new_last, rd->new_parent, + lookup_flags | target_flags, TASK_NORMAL); err =3D PTR_ERR(d2); if (IS_ERR(d2)) goto out_dput_d1; =20 + trap =3D lock_rename(rd->old_parent, rd->new_parent); + err =3D PTR_ERR(trap); + if (IS_ERR(trap)) + goto out_unlock; + + if (unlikely((!d_in_lookup(d1) && d_unhashed(d1)) || d1->d_parent !=3D rd= ->old_parent || + (!d_in_lookup(d2) && d_unhashed(d2)) || d2->d_parent !=3D rd->new_p= arent)) { + unlock_rename(rd->old_parent, rd->new_parent); + d_lookup_done(d1); dput(d1); + d_lookup_done(d2); dput(d2); + dput(trap); + goto retry; + } + if (d1 =3D=3D trap) { /* source is an ancestor of target */ err =3D -EINVAL; - goto out_dput_d2; + goto out_unlock; } =20 if (d2 =3D=3D trap) { @@ -3858,7 +3896,7 @@ __start_renaming(struct renamedata *rd, int lookup_fl= ags, err =3D -EINVAL; else err =3D -ENOTEMPTY; - goto out_dput_d2; + goto out_unlock; } =20 rd->old_dentry =3D d1; @@ -3866,14 +3904,14 @@ __start_renaming(struct renamedata *rd, int lookup_= flags, dget(rd->old_parent); return 0; =20 -out_dput_d2: +out_unlock: + unlock_rename(rd->old_parent, rd->new_parent); d_lookup_done(d2); dput(d2); out_dput_d1: d_lookup_done(d1); dput(d1); -out_unlock: - unlock_rename(rd->old_parent, rd->new_parent); +out_err: return err; } =20 @@ -3927,10 +3965,22 @@ __start_renaming_dentry(struct renamedata *rd, int = lookup_flags, if (rd->flags & RENAME_NOREPLACE) target_flags |=3D LOOKUP_EXCL; =20 - /* Already have the dentry - need to be sure to lock the correct parent */ +retry: + d2 =3D lookup_one_qstr(new_last, rd->new_parent, + lookup_flags | target_flags, TASK_NORMAL); + err =3D PTR_ERR(d2); + if (IS_ERR(d2)) + goto out_unlock; + + /* + * Already have the old_dentry - need to be sure to lock + * the correct parent + */ trap =3D lock_rename_child(old_dentry, rd->new_parent); + err =3D PTR_ERR(trap); if (IS_ERR(trap)) - return PTR_ERR(trap); + goto out_dput_d2; + if (d_unhashed(old_dentry) || (rd->old_parent && rd->old_parent !=3D old_dentry->d_parent)) { /* dentry was removed, or moved and explicit parent requested */ @@ -3938,16 +3988,19 @@ __start_renaming_dentry(struct renamedata *rd, int = lookup_flags, goto out_unlock; } =20 - d2 =3D lookup_one_qstr_excl(new_last, rd->new_parent, - lookup_flags | target_flags); - err =3D PTR_ERR(d2); - if (IS_ERR(d2)) - goto out_unlock; + if (unlikely((!d_in_lookup(d2) && d_unhashed(d2)) || + d2->d_parent !=3D rd->new_parent)) { + /* d2 was moved/removed before lock - repeat lookup */ + unlock_rename(old_dentry->d_parent, rd->new_parent); + d_lookup_done(d2); dput(d2); + dput(trap); + goto retry; + } =20 if (old_dentry =3D=3D trap) { /* source is an ancestor of target */ err =3D -EINVAL; - goto out_dput_d2; + goto out_unlock; } =20 if (d2 =3D=3D trap) { @@ -3956,7 +4009,7 @@ __start_renaming_dentry(struct renamedata *rd, int lo= okup_flags, err =3D -EINVAL; else err =3D -ENOTEMPTY; - goto out_dput_d2; + goto out_unlock; } =20 rd->old_dentry =3D dget(old_dentry); @@ -3964,11 +4017,11 @@ __start_renaming_dentry(struct renamedata *rd, int = lookup_flags, rd->old_parent =3D dget(old_dentry->d_parent); return 0; =20 +out_unlock: + unlock_rename(old_dentry->d_parent, rd->new_parent); out_dput_d2: d_lookup_done(d2); dput(d2); -out_unlock: - unlock_rename(old_dentry->d_parent, rd->new_parent); return err; } =20 @@ -4319,8 +4372,19 @@ static struct dentry *atomic_open(const struct path = *path, struct dentry *dentry =20 file->__f_path.dentry =3D DENTRY_NOT_SET; file->__f_path.mnt =3D path->mnt; - error =3D dir->i_op->atomic_open(dir, dentry, file, - open_to_namei_flags(open_flag), mode); + + if (open_flag & O_CREAT) + inode_lock(dir); + else + inode_lock_shared(dir); + if (dentry->d_inode) + error =3D finish_no_open(file, NULL); + else if (unlikely(IS_DEADDIR(dir))) + error =3D -ENOENT; + else + error =3D dir->i_op->atomic_open(dir, dentry, file, + open_to_namei_flags(open_flag), + mode); d_lookup_done(dentry); if (!error) { if (file->f_mode & FMODE_OPENED) { @@ -4339,6 +4403,13 @@ static struct dentry *atomic_open(const struct path = *path, struct dentry *dentry error =3D -ENOENT; } } + if (!error && (file->f_mode & FMODE_CREATED)) + fsnotify_create(dir, dentry); + if (open_flag & O_CREAT) + inode_unlock(dir); + else + inode_unlock_shared(dir); + if (error) { dput(dentry); dentry =3D ERR_PTR(error); @@ -4372,10 +4443,6 @@ static struct dentry *lookup_open(struct nameidata *= nd, struct file *file, struct dentry *dentry; int error, create_error =3D 0; umode_t mode =3D op->mode; - unsigned int shared_flag =3D (op->open_flag & O_CREAT) ? 0 : LOOKUP_SHARE= D; - - if (unlikely(IS_DEADDIR(dir_inode))) - return ERR_PTR(-ENOENT); =20 file->f_mode &=3D ~FMODE_CREATED; dentry =3D d_lookup(dir, &nd->last); @@ -4420,7 +4487,7 @@ static struct dentry *lookup_open(struct nameidata *n= d, struct file *file, if (open_flag & O_CREAT) { if (open_flag & O_EXCL) open_flag &=3D ~O_TRUNC; - mode =3D vfs_prepare_mode(idmap, dir->d_inode, mode, mode, mode); + mode =3D vfs_prepare_mode(idmap, dir_inode, mode, mode, mode); if (likely(got_write)) create_error =3D may_o_create(idmap, &nd->path, dentry, mode); @@ -4439,8 +4506,15 @@ static struct dentry *lookup_open(struct nameidata *= nd, struct file *file, } =20 if (d_in_lookup(dentry)) { - struct dentry *res =3D dir_inode->i_op->lookup(dir_inode, dentry, - nd->flags | shared_flag); + struct dentry *res; + + inode_lock_shared(dir_inode); + if (IS_DEADDIR(dir_inode)) + res =3D ERR_PTR(-ENOENT); + else + res =3D dir_inode->i_op->lookup(dir_inode, dentry, + nd->flags | LOOKUP_SHARED); + inode_unlock_shared(dir_inode); d_lookup_done(dentry); if (unlikely(res)) { if (IS_ERR(res)) { @@ -4459,15 +4533,22 @@ static struct dentry *lookup_open(struct nameidata = *nd, struct file *file, if (error) goto out_dput; =20 - file->f_mode |=3D FMODE_CREATED; - audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE); - if (!dir_inode->i_op->create) { - error =3D -EACCES; - goto out_dput; - } + inode_lock(dir_inode); + if (!dentry->d_inode && !unlikely(IS_DEADDIR(dir_inode))) { + file->f_mode |=3D FMODE_CREATED; + audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE); + if (!dir_inode->i_op->create) { + error =3D -EACCES; + goto out_dput; + } =20 - error =3D dir_inode->i_op->create(idmap, dir_inode, dentry, - mode, open_flag & O_EXCL); + error =3D dir_inode->i_op->create(idmap, dir_inode, dentry, + mode, open_flag & O_EXCL); + if (!error) + fsnotify_create(dir_inode, dentry); + } else if (!dentry->d_inode) + error =3D -ENOENT; + inode_unlock(dir_inode); if (error) goto out_dput; } @@ -4522,7 +4603,6 @@ static const char *open_last_lookups(struct nameidata= *nd, struct file *file, const struct open_flags *op) { struct delegated_inode delegated_inode =3D { }; - struct dentry *dir =3D nd->path.dentry; int open_flag =3D op->open_flag; bool got_write =3D false; struct dentry *dentry; @@ -4562,22 +4642,11 @@ static const char *open_last_lookups(struct nameida= ta *nd, * dropping this one anyway. */ } - if (open_flag & O_CREAT) - inode_lock(dir->d_inode); - else - inode_lock_shared(dir->d_inode); dentry =3D lookup_open(nd, file, op, got_write, &delegated_inode); if (!IS_ERR(dentry)) { - if (file->f_mode & FMODE_CREATED) - fsnotify_create(dir->d_inode, dentry); if (file->f_mode & FMODE_OPENED) fsnotify_open(file); } - if (open_flag & O_CREAT) - inode_unlock(dir->d_inode); - else - inode_unlock_shared(dir->d_inode); - if (got_write) mnt_drop_write(nd->path.mnt); =20 --=20 2.50.0.107.gf914562f5916.dirty