]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mds: Fix straydn race between unlink/rename linkmerge
authorKotresh HR <khiremat@redhat.com>
Thu, 27 Feb 2025 14:28:24 +0000 (19:58 +0530)
committerKotresh HR <khiremat@redhat.com>
Tue, 4 Mar 2025 06:20:47 +0000 (11:50 +0530)
Handle race between unlink of secondary hardlink (say hl_file1) and
linkmerge rename, triggered by unlink of primary (say file1) on to
the same secondary hardlink (hl_file1).
           1. ln file1 hl_file1
           2. unlink hl_file1
           3. prepares straydn for referent inode
           4. wait for the locks

   unlink file1, triggers linkmerge rename
   rename(hl_file1, primary_stray) ---> referent straydn converted to primary

           5. On retry, it find itself as primary,
              needs to prepare straydn for primary.
              so clean up the earlier referent straydn

Fixes: https://tracker.ceph.com/issues/54205
Signed-off-by: Kotresh HR <khiremat@redhat.com>
src/mds/Mutation.h
src/mds/Server.cc

index f1b7172fa12915f683acd1f8133284ad74751736..5a76657757fb698bf2e156f0291bf7fd4fe8471b 100644 (file)
@@ -489,6 +489,9 @@ struct MDRequestImpl : public MutationImpl {
   // indicator for vxattr osdmap update
   bool waited_for_osdmap = false;
 
+  // referent straydn
+  bool referent_straydn = false;
+
 protected:
   void _dump(ceph::Formatter *f) const override {
     _dump(f, false);
index 4f2fc1c029684b02a648d0788a2ebf94026cf2fa..8a43f1e55cfc95402ab8b7b8b26c5cc30967c1de 100644 (file)
@@ -8509,14 +8509,37 @@ void Server::handle_client_unlink(const MDRequestRef& mdr)
   // -- create stray dentry? --
   CDentry *straydn = NULL;
   if (dnl->is_primary()) {
+    /* Handle race between unlink of secondary hardlink (say hl_file1) and
+     * linkmerge rename, triggered by unlink of primary (say file1) on to
+     * the same secondary hardlink (hl_file1).
+     *           1. unlink hl_file1
+     *           2. prepares straydn for referent inode
+     *           3. wait for the locks
+     *
+     *   unlink file1, triggers linkmerge rename
+     *   rename(hl_file1, primary_stray) ---> referent straydn converted to primary
+     *
+     *           4. On retry, it find itself as primary,
+     *              needs to prepare straydn for primary.
+     *              so clean up the earlier referent straydn
+     */
+    if (mdr->straydn && mdr->referent_straydn) {
+      dout(10) << __func__ << " race, clean up referent straydn " << *mdr->straydn << dendl;
+      mdr->unpin(mdr->straydn);
+      mdr->straydn = nullptr;
+    }
     straydn = prepare_stray_dentry(mdr, dnl->get_inode());
     if (!straydn)
       return;
+    mdr->referent_straydn = false;
     dout(10) << " straydn is " << *straydn << dendl;
   } else if (dnl->is_referent_remote()) {
+    // Above race is not applicable here since a referent can get converted
+    // to primary but not the otherway.
     straydn = prepare_stray_dentry(mdr, dnl->get_referent_inode());
     if (!straydn)
       return;
+    mdr->referent_straydn = true;
     dout(10) << __func__ << " referent straydn is " << *straydn << dendl;
   } else if (mdr->straydn) {
     mdr->unpin(mdr->straydn);
@@ -9369,11 +9392,13 @@ void Server::handle_client_rename(const MDRequestRef& mdr)
     straydn = prepare_stray_dentry(mdr, destdnl->get_inode());
     if (!straydn)
       return;
+    mdr->referent_straydn = false;
     dout(10) << " straydn is " << *straydn << dendl;
   } else if (destdnl->is_referent_remote()) { //both linkmerge and non linkmerge case
     straydn = prepare_stray_dentry(mdr, destdnl->get_referent_inode());
     if (!straydn)
       return;
+    mdr->referent_straydn = true;
     dout(10) << __func__ << (linkmerge ? " linkmerge:yes ": " linkmerge:no ")
              << " referent straydn is " << *straydn << dendl;
   } else if (mdr->straydn) {