From 1b642e1912bdce08c4f878138c2665c878228b25 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 2 Aug 2022 14:19:59 +0800 Subject: [PATCH] mds: wait the linkmerge/migrate to finish after unlink If one inode has more than one link and after one of its dentries being unlinked it will be moved to stray directory. Before the linkmerge/migrate finises if a link request comes it will fail with -EXDEV. While in non-multiple link case it's also possible that the clients could pass one invalidate ino, which is still under unlinking. Just wait the linkmerge/migrate or purge to finish. https://tracker.ceph.com/issues/56695 Signed-off-by: Xiubo Li (cherry picked from commit 48f9a8990b402daa0f0f9109bc4b0087c01b33c6) --- src/mds/CInode.h | 3 ++- src/mds/Server.cc | 21 +++++++++++++++++++++ src/mds/StrayManager.cc | 11 +++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/mds/CInode.h b/src/mds/CInode.h index a2b034ec647c9..3f18ab0fe864b 100644 --- a/src/mds/CInode.h +++ b/src/mds/CInode.h @@ -398,7 +398,8 @@ class CInode : public MDSCacheObject, public InodeStoreBase, public Counterinode && target_realm->get_subvolume_ino() != dir->inode->find_snaprealm()->get_subvolume_ino()) { + if (target_pin->is_stray()) { + mds->locker->drop_locks(mdr.get()); + targeti->add_waiter(CInode::WAIT_UNLINK, + new C_MDS_RetryRequest(mdcache, mdr)); + mdlog->flush(); + return; + } dout(7) << "target is in different subvolume, failing..." << dendl; respond_to_request(mdr, -CEPHFS_EXDEV); return; @@ -9024,6 +9031,12 @@ void Server::handle_client_rename(MDRequestRef& mdr) C_MDS_rename_finish *fin = new C_MDS_rename_finish(this, mdr, srcdn, destdn, straydn); journal_and_reply(mdr, srci, destdn, le, fin); + + // trigger to flush mdlog in case reintegrating or migrating the stray dn, + // because the link requests maybe waiting. + if (srcdn->get_dir()->inode->is_stray()) { + mdlog->flush(); + } mds->balancer->maybe_fragment(destdn->get_dir(), false); } @@ -9624,6 +9637,14 @@ void Server::_rename_apply(MDRequestRef& mdr, CDentry *srcdn, CDentry *destdn, C srcdn->get_dir()->unlink_inode(srcdn); + // After the stray dn being unlinked from the corresponding inode in case of + // reintegrate_stray/migrate_stray, just wake up the waitiers. + MDSContext::vec finished; + in->take_waiting(CInode::WAIT_UNLINK, finished); + if (!finished.empty()) { + mds->queue_waiters(finished); + } + // dest if (srcdn_was_remote) { if (!linkmerge) { diff --git a/src/mds/StrayManager.cc b/src/mds/StrayManager.cc index ab8f0ddb49e26..4aae3edead8f1 100644 --- a/src/mds/StrayManager.cc +++ b/src/mds/StrayManager.cc @@ -281,6 +281,17 @@ void StrayManager::_purge_stray_logged(CDentry *dn, version_t pdv, MutationRef& dir->remove_dentry(dn); } + // Once we are here normally the waiter list are mostly empty + // but in corner case that the clients pass a invalidate ino, + // which maybe under unlinking, the link caller will add the + // request to the waiter list. We need try to wake them up + // anyway. + MDSContext::vec finished; + in->take_waiting(CInode::WAIT_UNLINK, finished); + if (!finished.empty()) { + mds->queue_waiters(finished); + } + // drop inode inodeno_t ino = in->ino(); if (in->is_dirty()) -- 2.39.5