From f264f11f44d6355452dadc2a20efddc012c531e3 Mon Sep 17 00:00:00 2001 From: Kotresh HR Date: Thu, 27 Feb 2025 19:58:24 +0530 Subject: [PATCH] mds: Fix straydn race between unlink/rename linkmerge 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 --- src/mds/Mutation.h | 3 +++ src/mds/Server.cc | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/mds/Mutation.h b/src/mds/Mutation.h index f1b7172fa1291..5a76657757fb6 100644 --- a/src/mds/Mutation.h +++ b/src/mds/Mutation.h @@ -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); diff --git a/src/mds/Server.cc b/src/mds/Server.cc index 4f2fc1c029684..8a43f1e55cfc9 100644 --- a/src/mds/Server.cc +++ b/src/mds/Server.cc @@ -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) { -- 2.39.5