@@ -856,6 +856,10 @@ static int ceph_mknod(struct user_namespace *mnt_userns, struct inode *dir,
856856 if (ceph_snap (dir ) != CEPH_NOSNAP )
857857 return - EROFS ;
858858
859+ err = ceph_wait_on_conflict_unlink (dentry );
860+ if (err )
861+ return err ;
862+
859863 if (ceph_quota_is_max_files_exceeded (dir )) {
860864 err = - EDQUOT ;
861865 goto out ;
@@ -918,6 +922,10 @@ static int ceph_symlink(struct user_namespace *mnt_userns, struct inode *dir,
918922 if (ceph_snap (dir ) != CEPH_NOSNAP )
919923 return - EROFS ;
920924
925+ err = ceph_wait_on_conflict_unlink (dentry );
926+ if (err )
927+ return err ;
928+
921929 if (ceph_quota_is_max_files_exceeded (dir )) {
922930 err = - EDQUOT ;
923931 goto out ;
@@ -968,9 +976,13 @@ static int ceph_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
968976 struct ceph_mds_client * mdsc = ceph_sb_to_mdsc (dir -> i_sb );
969977 struct ceph_mds_request * req ;
970978 struct ceph_acl_sec_ctx as_ctx = {};
971- int err = - EROFS ;
979+ int err ;
972980 int op ;
973981
982+ err = ceph_wait_on_conflict_unlink (dentry );
983+ if (err )
984+ return err ;
985+
974986 if (ceph_snap (dir ) == CEPH_SNAPDIR ) {
975987 /* mkdir .snap/foo is a MKSNAP */
976988 op = CEPH_MDS_OP_MKSNAP ;
@@ -980,6 +992,7 @@ static int ceph_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
980992 dout ("mkdir dir %p dn %p mode 0%ho\n" , dir , dentry , mode );
981993 op = CEPH_MDS_OP_MKDIR ;
982994 } else {
995+ err = - EROFS ;
983996 goto out ;
984997 }
985998
@@ -1037,6 +1050,10 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir,
10371050 struct ceph_mds_request * req ;
10381051 int err ;
10391052
1053+ err = ceph_wait_on_conflict_unlink (dentry );
1054+ if (err )
1055+ return err ;
1056+
10401057 if (ceph_snap (dir ) != CEPH_NOSNAP )
10411058 return - EROFS ;
10421059
@@ -1071,31 +1088,49 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir,
10711088static void ceph_async_unlink_cb (struct ceph_mds_client * mdsc ,
10721089 struct ceph_mds_request * req )
10731090{
1091+ struct dentry * dentry = req -> r_dentry ;
1092+ struct ceph_fs_client * fsc = ceph_sb_to_client (dentry -> d_sb );
1093+ struct ceph_dentry_info * di = ceph_dentry (dentry );
10741094 int result = req -> r_err ? req -> r_err :
10751095 le32_to_cpu (req -> r_reply_info .head -> result );
10761096
1097+ if (!test_bit (CEPH_DENTRY_ASYNC_UNLINK_BIT , & di -> flags ))
1098+ pr_warn ("%s dentry %p:%pd async unlink bit is not set\n" ,
1099+ __func__ , dentry , dentry );
1100+
1101+ spin_lock (& fsc -> async_unlink_conflict_lock );
1102+ hash_del_rcu (& di -> hnode );
1103+ spin_unlock (& fsc -> async_unlink_conflict_lock );
1104+
1105+ spin_lock (& dentry -> d_lock );
1106+ di -> flags &= ~CEPH_DENTRY_ASYNC_UNLINK ;
1107+ wake_up_bit (& di -> flags , CEPH_DENTRY_ASYNC_UNLINK_BIT );
1108+ spin_unlock (& dentry -> d_lock );
1109+
1110+ synchronize_rcu ();
1111+
10771112 if (result == - EJUKEBOX )
10781113 goto out ;
10791114
10801115 /* If op failed, mark everyone involved for errors */
10811116 if (result ) {
10821117 int pathlen = 0 ;
10831118 u64 base = 0 ;
1084- char * path = ceph_mdsc_build_path (req -> r_dentry , & pathlen ,
1119+ char * path = ceph_mdsc_build_path (dentry , & pathlen ,
10851120 & base , 0 );
10861121
10871122 /* mark error on parent + clear complete */
10881123 mapping_set_error (req -> r_parent -> i_mapping , result );
10891124 ceph_dir_clear_complete (req -> r_parent );
10901125
10911126 /* drop the dentry -- we don't know its status */
1092- if (!d_unhashed (req -> r_dentry ))
1093- d_drop (req -> r_dentry );
1127+ if (!d_unhashed (dentry ))
1128+ d_drop (dentry );
10941129
10951130 /* mark inode itself for an error (since metadata is bogus) */
10961131 mapping_set_error (req -> r_old_inode -> i_mapping , result );
10971132
1098- pr_warn ("ceph: async unlink failure path=(%llx)%s result=%d!\n" ,
1133+ pr_warn ("async unlink failure path=(%llx)%s result=%d!\n" ,
10991134 base , IS_ERR (path ) ? "<<bad>>" : path , result );
11001135 ceph_mdsc_free_path (path , pathlen );
11011136 }
@@ -1180,13 +1215,25 @@ static int ceph_unlink(struct inode *dir, struct dentry *dentry)
11801215
11811216 if (try_async && op == CEPH_MDS_OP_UNLINK &&
11821217 (req -> r_dir_caps = get_caps_for_async_unlink (dir , dentry ))) {
1218+ struct ceph_dentry_info * di = ceph_dentry (dentry );
1219+
11831220 dout ("async unlink on %llu/%.*s caps=%s" , ceph_ino (dir ),
11841221 dentry -> d_name .len , dentry -> d_name .name ,
11851222 ceph_cap_string (req -> r_dir_caps ));
11861223 set_bit (CEPH_MDS_R_ASYNC , & req -> r_req_flags );
11871224 req -> r_callback = ceph_async_unlink_cb ;
11881225 req -> r_old_inode = d_inode (dentry );
11891226 ihold (req -> r_old_inode );
1227+
1228+ spin_lock (& dentry -> d_lock );
1229+ di -> flags |= CEPH_DENTRY_ASYNC_UNLINK ;
1230+ spin_unlock (& dentry -> d_lock );
1231+
1232+ spin_lock (& fsc -> async_unlink_conflict_lock );
1233+ hash_add_rcu (fsc -> async_unlink_conflict , & di -> hnode ,
1234+ dentry -> d_name .hash );
1235+ spin_unlock (& fsc -> async_unlink_conflict_lock );
1236+
11901237 err = ceph_mdsc_submit_request (mdsc , dir , req );
11911238 if (!err ) {
11921239 /*
@@ -1195,10 +1242,20 @@ static int ceph_unlink(struct inode *dir, struct dentry *dentry)
11951242 */
11961243 drop_nlink (inode );
11971244 d_delete (dentry );
1198- } else if (err == - EJUKEBOX ) {
1199- try_async = false;
1200- ceph_mdsc_put_request (req );
1201- goto retry ;
1245+ } else {
1246+ spin_lock (& fsc -> async_unlink_conflict_lock );
1247+ hash_del_rcu (& di -> hnode );
1248+ spin_unlock (& fsc -> async_unlink_conflict_lock );
1249+
1250+ spin_lock (& dentry -> d_lock );
1251+ di -> flags &= ~CEPH_DENTRY_ASYNC_UNLINK ;
1252+ spin_unlock (& dentry -> d_lock );
1253+
1254+ if (err == - EJUKEBOX ) {
1255+ try_async = false;
1256+ ceph_mdsc_put_request (req );
1257+ goto retry ;
1258+ }
12021259 }
12031260 } else {
12041261 set_bit (CEPH_MDS_R_PARENT_LOCKED , & req -> r_req_flags );
@@ -1237,6 +1294,10 @@ static int ceph_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
12371294 (!ceph_quota_is_same_realm (old_dir , new_dir )))
12381295 return - EXDEV ;
12391296
1297+ err = ceph_wait_on_conflict_unlink (new_dentry );
1298+ if (err )
1299+ return err ;
1300+
12401301 dout ("rename dir %p dentry %p to dir %p dentry %p\n" ,
12411302 old_dir , old_dentry , new_dir , new_dentry );
12421303 req = ceph_mdsc_create_request (mdsc , op , USE_AUTH_MDS );
0 commit comments