From 801951e8c0d62dbbe724ce506fb44bc809bb7d4f Mon Sep 17 00:00:00 2001 From: Igor Golikov Date: Sun, 16 Feb 2025 08:39:53 +0000 Subject: [PATCH] mds: implement new asok command for dumping stray folder Signed-off-by: Igor Golikov Fixes: https://tracker.ceph.com/issues/56442 --- src/mds/MDCache.cc | 49 ++++++++++++++++++++++++++++++++++---------- src/mds/MDCache.h | 37 ++++++++++++++++++++++++++++++++- src/mds/MDSDaemon.cc | 5 +++++ src/mds/MDSRank.cc | 19 ++++++++++++++++- 4 files changed, 97 insertions(+), 13 deletions(-) diff --git a/src/mds/MDCache.cc b/src/mds/MDCache.cc index eb5b6485d53..bbb55f44845 100644 --- a/src/mds/MDCache.cc +++ b/src/mds/MDCache.cc @@ -10211,13 +10211,21 @@ void MDCache::notify_global_snaprealm_update(int snap_op) struct C_MDC_RetryScanStray : public MDCacheContext { dirfrag_t next; - C_MDC_RetryScanStray(MDCache *c, dirfrag_t n) : MDCacheContext(c), next(n) { } + std::unique_ptr cmd_ctx; + C_MDC_RetryScanStray(MDCache *c, dirfrag_t n, std::unique_ptr ctx) : + MDCacheContext(c), next(n), cmd_ctx(std::move(ctx)) {} void finish(int r) override { - mdcache->scan_stray_dir(next); + mdcache->scan_stray_dir(next, std::move(cmd_ctx)); } }; -void MDCache::scan_stray_dir(dirfrag_t next) +/* + * If the cmd_ctx is not nullptr, the caller is asok command handler, + * which will block until the on_finish will be called. + * The cmd_ctx holds the formatter to dump stray dir content while scanning. + * The function can return EAGAIN, to make possible waiting semantics clear. +*/ +int MDCache::scan_stray_dir(dirfrag_t next, std::unique_ptr cmd_ctx) { dout(10) << "scan_stray_dir " << next << dendl; @@ -10236,13 +10244,13 @@ void MDCache::scan_stray_dir(dirfrag_t next) continue; if (!dir->can_auth_pin()) { - dir->add_waiter(CDir::WAIT_UNFREEZE, new C_MDC_RetryScanStray(this, dir->dirfrag())); - return; + dir->add_waiter(CDir::WAIT_UNFREEZE, new C_MDC_RetryScanStray(this, dir->dirfrag(), std::move(cmd_ctx))); + return -EAGAIN; } if (!dir->is_complete()) { - dir->fetch(new C_MDC_RetryScanStray(this, dir->dirfrag())); - return; + dir->fetch(new C_MDC_RetryScanStray(this, dir->dirfrag(), std::move(cmd_ctx))); + return -EAGAIN; } for (auto &p : dir->items) { @@ -10251,14 +10259,32 @@ void MDCache::scan_stray_dir(dirfrag_t next) CDentry::linkage_t *dnl = dn->get_projected_linkage(); if (dnl->is_primary()) { CInode *in = dnl->get_inode(); + // only if we came from asok cmd handler + if (cmd_ctx) { + cmd_ctx->begin_dump(); + cmd_ctx->get_formatter()->open_object_section("stray_inode"); + cmd_ctx->get_formatter()->dump_int("ino: ", in->ino()); + cmd_ctx->get_formatter()->dump_string("stray_prior_path: ", in->get_inode()->stray_prior_path); + in->dump(cmd_ctx->get_formatter(), CInode::DUMP_CAPS); + cmd_ctx->get_formatter()->close_section(); + } if (in->get_inode()->nlink == 0) in->state_set(CInode::STATE_ORPHAN); - maybe_eval_stray(in); + // no need to evaluate stray when dumping the dir content + if (!cmd_ctx) { + maybe_eval_stray(in); + } } } } next.frag = frag_t(); } + // only if we came from asok cmd handler + if (cmd_ctx) { + cmd_ctx->end_dump(); + cmd_ctx->finish(0); + } + return 0; } void MDCache::fetch_backtrace(inodeno_t ino, int64_t pool, bufferlist& bl, Context *fin) @@ -10269,9 +10295,10 @@ void MDCache::fetch_backtrace(inodeno_t ino, int64_t pool, bufferlist& bl, Conte mds->logger->inc(l_mds_openino_backtrace_fetch); } - - - +int MDCache::stray_status(std::unique_ptr ctx) +{ + return scan_stray_dir(dirfrag_t(), std::move(ctx)); +} // ======================================================================================== // DISCOVER diff --git a/src/mds/MDCache.h b/src/mds/MDCache.h index 5e2af9c2659..f7008730ac9 100644 --- a/src/mds/MDCache.h +++ b/src/mds/MDCache.h @@ -641,6 +641,40 @@ private: bool ap_freeze = false; }; + /** + * Helper wrapper, provides both context object with finish function + * and placeholder for formatter. Better alternative to passing formatter as another argument + * to the MDSCache function + */ + class C_MDS_DumpStrayDirCtx : public MDSInternalContext { + public: + void finish(int r) override { + ceph_assert(on_finish); + MDSContext::finish(r); + on_finish(r); + } + Formatter* get_formatter() const { + ceph_assert(dump_formatter); + return dump_formatter; + } + void begin_dump() { + if(!started) { + started = true; + get_formatter()->open_array_section("strays"); + } + } + void end_dump() { + get_formatter()->close_section(); + } + C_MDS_DumpStrayDirCtx(MDCache *c, Formatter* f, std::function&& ext_on_finish) : + MDSInternalContext(c->mds), cache(c), dump_formatter(f), on_finish(std::move(ext_on_finish)) {} + private: + MDCache *cache; + Formatter* dump_formatter; + std::function on_finish; + bool started = false; + }; + MDRequestRef lock_path(LockPathConfig config, std::function on_locked = {}); void clean_open_file_lists(); @@ -1072,6 +1106,7 @@ private: void dump_tree(CInode *in, const int cur_depth, const int max_depth, Formatter *f); void cache_status(Formatter *f); + int stray_status(std::unique_ptr ctx); void dump_resolve_status(Formatter *f) const; void dump_rejoin_status(Formatter *f) const; @@ -1271,7 +1306,7 @@ private: void handle_open_ino(const cref_t &m, int err=0); void handle_open_ino_reply(const cref_t &m); - void scan_stray_dir(dirfrag_t next=dirfrag_t()); + int scan_stray_dir(dirfrag_t next=dirfrag_t(), std::unique_ptr ctx = nullptr); // -- replicas -- void handle_discover(const cref_t &dis); void handle_discover_reply(const cref_t &m); diff --git a/src/mds/MDSDaemon.cc b/src/mds/MDSDaemon.cc index 5f1a864f2a3..40017dd8b1e 100644 --- a/src/mds/MDSDaemon.cc +++ b/src/mds/MDSDaemon.cc @@ -544,6 +544,11 @@ void MDSDaemon::set_up_admin_socket() asok_hook, "run cpu profiling on daemon"); ceph_assert(r == 0); + r = admin_socket->register_command( + "dump stray", + asok_hook, + "dump stray folder content"); + ceph_assert(r == 0); } void MDSDaemon::clean_up_admin_socket() diff --git a/src/mds/MDSRank.cc b/src/mds/MDSRank.cc index bc0211fac7c..9221f1608c8 100644 --- a/src/mds/MDSRank.cc +++ b/src/mds/MDSRank.cc @@ -3070,6 +3070,23 @@ void MDSRankDispatcher::handle_asok_command( } else if (command == "quiesce db") { command_quiesce_db(cmdmap, on_finish); return; + } else if (command == "dump stray") { + dout(10) << "dump_stray start" << dendl; + // the context is a wrapper for formatter to be used while scanning stray dir + auto context = std::make_unique(mdcache, f, + [this,on_finish](int r) { + // completion callback, will be called when scan is done + dout(10) << "dump_stray done" << dendl; + bufferlist bl; + on_finish(r, "", bl); + }); + std::lock_guard l(mds_lock); + r = mdcache->stray_status(std::move(context)); + // since the scanning op can be async, we want to know it, for better semantics + if (r == -EAGAIN) { + dout(10) << "dump_stray wait" << dendl; + } + return; } else { r = -ENOSYS; } @@ -3506,7 +3523,7 @@ void MDSRank::command_quiesce_path(Formatter* f, const cmdmap_t& cmdmap, asok_fi // This is a little ugly, apologies. // We should still be under the mds lock for this test to be valid. - // MDCache will delete the quiesce_ctx if it manages to complete syncrhonously, + // MDCache will delete the quiesce_ctx if it manages // so we are testing the `mdr->internal_op_finish` to see if that has happend if (!await && mdr && mdr->internal_op_finish) { ceph_assert(mdr->internal_op_finish == quiesce_ctx); -- 2.39.5