From: Xiubo Li Date: Tue, 2 Aug 2022 06:19:59 +0000 (+0800) Subject: mds: wait the linkmerge/migrate to finish after unlink X-Git-Tag: v19.0.0~1405^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=48f9a8990b402daa0f0f9109bc4b0087c01b33c6;p=ceph.git 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 --- diff --git a/src/mds/CInode.h b/src/mds/CInode.h index a2b034ec647..3f18ab0fe86 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; @@ -9028,6 +9035,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); } @@ -9628,6 +9641,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 ab8f0ddb49e..4aae3edead8 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())