struct C_MDC_RetryScanStray : public MDCacheContext {
dirfrag_t next;
- C_MDC_RetryScanStray(MDCache *c, dirfrag_t n) : MDCacheContext(c), next(n) { }
+ std::unique_ptr<MDCache::C_MDS_DumpStrayDirCtx> cmd_ctx;
+ C_MDC_RetryScanStray(MDCache *c, dirfrag_t n, std::unique_ptr<MDCache::C_MDS_DumpStrayDirCtx> 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<MDCache::C_MDS_DumpStrayDirCtx> cmd_ctx)
{
dout(10) << "scan_stray_dir " << next << dendl;
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) {
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)
mds->logger->inc(l_mds_openino_backtrace_fetch);
}
-
-
-
+int MDCache::stray_status(std::unique_ptr<C_MDS_DumpStrayDirCtx> ctx)
+{
+ return scan_stray_dir(dirfrag_t(), std::move(ctx));
+}
// ========================================================================================
// DISCOVER
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<void(int)>&& 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<void(int)> on_finish;
+ bool started = false;
+ };
+
MDRequestRef lock_path(LockPathConfig config, std::function<void(MDRequestRef const& mdr)> on_locked = {});
void clean_open_file_lists();
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<C_MDS_DumpStrayDirCtx> ctx);
void dump_resolve_status(Formatter *f) const;
void dump_rejoin_status(Formatter *f) const;
void handle_open_ino(const cref_t<MMDSOpenIno> &m, int err=0);
void handle_open_ino_reply(const cref_t<MMDSOpenInoReply> &m);
- void scan_stray_dir(dirfrag_t next=dirfrag_t());
+ int scan_stray_dir(dirfrag_t next=dirfrag_t(), std::unique_ptr<C_MDS_DumpStrayDirCtx> ctx = nullptr);
// -- replicas --
void handle_discover(const cref_t<MDiscover> &dis);
void handle_discover_reply(const cref_t<MDiscoverReply> &m);
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()
} 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::C_MDS_DumpStrayDirCtx>(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;
}
// 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);