Skip to content

Commit 177a7e0

Browse files
author
Alex Markuze
committed
ceph: fix client race condition validating r_parent before applying state
JIRA: https://issues.redhat.com/browse/RHEL-109212 Conflicts: Centos missing 197b7d7 adjesent API is different Centos missing 2827bad obviates some function chnages commit 15f519e Author: Alex Markuze <amarkuze@redhat.com> Date: Tue Aug 12 09:57:38 2025 +0000 ceph: fix race condition validating r_parent before applying state Add validation to ensure the cached parent directory inode matches the directory info in MDS replies. This prevents client-side race conditions where concurrent operations (e.g. rename) cause r_parent to become stale between request initiation and reply processing, which could lead to applying state changes to incorrect directory inodes. [ idryomov: folded a kerneldoc fixup and a follow-up fix from Alex to move CEPH_CAP_PIN reference when r_parent is updated: When the parent directory lock is not held, req->r_parent can become stale and is updated to point to the correct inode. However, the associated CEPH_CAP_PIN reference was not being adjusted. The CEPH_CAP_PIN is a reference on an inode that is tracked for accounting purposes. Moving this pin is important to keep the accounting balanced. When the pin was not moved from the old parent to the new one, it created two problems: The reference on the old, stale parent was never released, causing a reference leak. A reference for the new parent was never acquired, creating the risk of a reference underflow later in ceph_mdsc_release_request(). This patch corrects the logic by releasing the pin from the old parent and acquiring it for the new parent when r_parent is switched. This ensures reference accounting stays balanced. ] Cc: stable@vger.kernel.org Signed-off-by: Alex Markuze <amarkuze@redhat.com> Reviewed-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com> Signed-off-by: Alex Markuze <amarkuze@redhat.com>
1 parent 0e16add commit 177a7e0

File tree

5 files changed

+131
-90
lines changed

5 files changed

+131
-90
lines changed

fs/ceph/debugfs.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ static int mdsc_show(struct seq_file *s, void *p)
5555
struct ceph_mds_client *mdsc = fsc->mdsc;
5656
struct ceph_mds_request *req;
5757
struct rb_node *rp;
58-
int pathlen = 0;
59-
u64 pathbase;
6058
char *path;
6159

6260
mutex_lock(&mdsc->mutex);
@@ -81,8 +79,8 @@ static int mdsc_show(struct seq_file *s, void *p)
8179
if (req->r_inode) {
8280
seq_printf(s, " #%llx", ceph_ino(req->r_inode));
8381
} else if (req->r_dentry) {
84-
path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
85-
&pathbase, 0);
82+
struct ceph_path_info path_info;
83+
path = ceph_mdsc_build_path(req->r_dentry, &path_info, 0);
8684
if (IS_ERR(path))
8785
path = NULL;
8886
spin_lock(&req->r_dentry->d_lock);
@@ -91,7 +89,7 @@ static int mdsc_show(struct seq_file *s, void *p)
9189
req->r_dentry,
9290
path ? path : "");
9391
spin_unlock(&req->r_dentry->d_lock);
94-
ceph_mdsc_free_path(path, pathlen);
92+
ceph_mdsc_free_path_info(&path_info);
9593
} else if (req->r_path1) {
9694
seq_printf(s, " #%llx/%s", req->r_ino1.ino,
9795
req->r_path1);
@@ -100,8 +98,8 @@ static int mdsc_show(struct seq_file *s, void *p)
10098
}
10199

102100
if (req->r_old_dentry) {
103-
path = ceph_mdsc_build_path(req->r_old_dentry, &pathlen,
104-
&pathbase, 0);
101+
struct ceph_path_info path_info;
102+
path = ceph_mdsc_build_path(req->r_old_dentry, &path_info, 0);
105103
if (IS_ERR(path))
106104
path = NULL;
107105
spin_lock(&req->r_old_dentry->d_lock);
@@ -111,7 +109,7 @@ static int mdsc_show(struct seq_file *s, void *p)
111109
req->r_old_dentry,
112110
path ? path : "");
113111
spin_unlock(&req->r_old_dentry->d_lock);
114-
ceph_mdsc_free_path(path, pathlen);
112+
ceph_mdsc_free_path_info(&path_info);
115113
} else if (req->r_path2 && req->r_op != CEPH_MDS_OP_SYMLINK) {
116114
if (req->r_ino2.ino)
117115
seq_printf(s, " #%llx/%s", req->r_ino2.ino,

fs/ceph/dir.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,10 +1224,8 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc,
12241224

12251225
/* If op failed, mark everyone involved for errors */
12261226
if (result) {
1227-
int pathlen = 0;
1228-
u64 base = 0;
1229-
char *path = ceph_mdsc_build_path(dentry, &pathlen,
1230-
&base, 0);
1227+
struct ceph_path_info path_info = {0};
1228+
char *path = ceph_mdsc_build_path(dentry, &path_info, 0);
12311229

12321230
/* mark error on parent + clear complete */
12331231
mapping_set_error(req->r_parent->i_mapping, result);
@@ -1241,8 +1239,8 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc,
12411239
mapping_set_error(req->r_old_inode->i_mapping, result);
12421240

12431241
pr_warn("async unlink failure path=(%llx)%s result=%d!\n",
1244-
base, IS_ERR(path) ? "<<bad>>" : path, result);
1245-
ceph_mdsc_free_path(path, pathlen);
1242+
path_info.vino.ino, IS_ERR(path) ? "<<bad>>" : path, result);
1243+
ceph_mdsc_free_path_info(&path_info);
12461244
}
12471245
out:
12481246
iput(req->r_old_inode);

fs/ceph/file.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -576,14 +576,12 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc,
576576
mapping_set_error(req->r_parent->i_mapping, result);
577577

578578
if (result) {
579-
int pathlen = 0;
580-
u64 base = 0;
581-
char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
582-
&base, 0);
579+
struct ceph_path_info path_info = {0};
580+
char *path = ceph_mdsc_build_path(req->r_dentry, &path_info, 0);
583581

584582
pr_warn("async create failure path=(%llx)%s result=%d!\n",
585-
base, IS_ERR(path) ? "<<bad>>" : path, result);
586-
ceph_mdsc_free_path(path, pathlen);
583+
path_info.vino.ino, IS_ERR(path) ? "<<bad>>" : path, result);
584+
ceph_mdsc_free_path_info(&path_info);
587585

588586
ceph_dir_clear_complete(req->r_parent);
589587
if (!d_unhashed(dentry))

0 commit comments

Comments
 (0)