bool strict_strtob(const char* str, std::string *err);
+long long strict_strtoll(std::string_view str, int base, std::string *err);
long long strict_strtoll(const char *str, int base, std::string *err);
+int strict_strtol(std::string_view str, int base, std::string *err);
int strict_strtol(const char *str, int base, std::string *err);
double strict_strtod(const char *str, std::string *err);
// FIXME: weird implicit construction, is someone else meant
// to be calling scrub_info_create first?
scrub_info();
- scrub_infop->header = header;
scrub_infop->directory_scrubbing = true;
+ scrub_infop->header = header;
+ header->inc_num_pending();
}
void CDir::scrub_aborted() {
dout(20) << __func__ << dendl;
ceph_assert(scrub_is_in_progress());
- scrub_infop->directory_scrubbing = false;
scrub_infop->last_scrub_dirty = false;
+ scrub_infop->directory_scrubbing = false;
+ scrub_infop->header->dec_num_pending();
scrub_infop.reset();
}
{
dout(20) << __func__ << dendl;
ceph_assert(scrub_is_in_progress());
- scrub_infop->directory_scrubbing = false;
scrub_infop->last_local.time = ceph_clock_now();
scrub_infop->last_local.version = get_version();
scrub_infop->last_recursive = scrub_infop->last_local;
scrub_infop->last_scrub_dirty = true;
+
+ scrub_infop->directory_scrubbing = false;
+ scrub_infop->header->dec_num_pending();
}
void CDir::scrub_maybe_delete_info()
* @post It has set up its internal scrubbing state.
*/
void scrub_initialize(const ScrubHeaderRef& header);
- ScrubHeaderRef get_scrub_header() {
- return scrub_infop ? scrub_infop->header : nullptr;
+ const ScrubHeaderRef& get_scrub_header() {
+ static const ScrubHeaderRef nullref;
+ return scrub_infop ? scrub_infop->header : nullref;
}
bool scrub_is_in_progress() const {
scrub_infop->scrub_in_progress = true;
scrub_infop->queued_frags.clear();
scrub_infop->header = header;
+ header->inc_num_pending();
// right now we don't handle remote inodes
}
ceph_assert(scrub_is_in_progress());
scrub_infop->scrub_in_progress = false;
+ scrub_infop->header->dec_num_pending();
scrub_maybe_delete_info();
}
scrub_infop->last_scrub_stamp = ceph_clock_now();
scrub_infop->last_scrub_dirty = true;
scrub_infop->scrub_in_progress = false;
+ scrub_infop->header->dec_num_pending();
}
int64_t CInode::get_backtrace_pool() const
return scrub_infop.get();
}
- ScrubHeaderRef get_scrub_header() {
- return scrub_infop ? scrub_infop->header : nullptr;
+ const ScrubHeaderRef& get_scrub_header() {
+ static const ScrubHeaderRef nullref;
+ return scrub_infop ? scrub_infop->header : nullref;
}
bool scrub_is_in_progress() const {
return r;
}
-
-
-C_MDS_RetryRequest::C_MDS_RetryRequest(MDCache *c, MDRequestRef& r)
- : MDSInternalContext(c->mds), cache(c), mdr(r)
-{}
-
void C_MDS_RetryRequest::finish(int r)
{
mdr->retry++;
cache->dispatch_request(mdr);
}
+MDSContext *CF_MDS_RetryRequestFactory::build()
+{
+ if (drop_locks) {
+ mdcache->mds->locker->drop_locks(mdr.get(), nullptr);
+ mdr->drop_local_auth_pins();
+ }
+ return new C_MDS_RetryRequest(mdcache, mdr);
+}
class C_MDS_EnqueueScrub : public Context
{
if (r == 0) {
// since recursive scrub is asynchronous, dump minimal output
// to not upset cli tools.
- if (header && header->get_recursive()) {
- formatter->open_object_section("results");
- formatter->dump_int("return_code", 0);
- formatter->dump_string("scrub_tag", tag);
- formatter->dump_string("mode", "asynchronous");
- formatter->close_section(); // results
- }
+ formatter->open_object_section("results");
+ formatter->dump_int("return_code", 0);
+ formatter->dump_string("scrub_tag", tag);
+ formatter->dump_string("mode", "asynchronous");
+ formatter->close_section(); // results
} else { // we failed the lookup or something; dump ourselves
formatter->open_object_section("results");
formatter->dump_int("return_code", r);
Formatter *f, Context *fin)
{
dout(10) << __func__ << " " << path << dendl;
- MDRequestRef mdr = request_start_internal(CEPH_MDS_OP_ENQUEUE_SCRUB);
- if (path == "~mdsdir") {
- filepath fp(MDS_INO_MDSDIR(mds->get_nodeid()));
- mdr->set_filepath(fp);
- } else {
- filepath fp(path);
- mdr->set_filepath(path);
+
+ filepath fp;
+ if (path.compare(0, 4, "~mds") == 0) {
+ mds_rank_t rank;
+ if (path == "~mdsdir") {
+ rank = mds->get_nodeid();
+ } else {
+ std::string err;
+ rank = strict_strtoll(path.substr(4), 10, &err);
+ if (!err.empty())
+ rank = MDS_RANK_NONE;
+ }
+ if (rank >= 0 && rank < MAX_MDS)
+ fp.set_path("", MDS_INO_MDSDIR(rank));
}
+ if (fp.get_ino() == inodeno_t(0))
+ fp.set_path(path);
+
+ MDRequestRef mdr = request_start_internal(CEPH_MDS_OP_ENQUEUE_SCRUB);
+ mdr->set_filepath(fp);
bool is_internal = false;
std::string tag_str(tag);
void MDCache::enqueue_scrub_work(MDRequestRef& mdr)
{
- CInode *in = mds->server->rdlock_path_pin_ref(mdr, true);
- if (NULL == in)
+ CInode *in;
+ CF_MDS_RetryRequestFactory cf(this, mdr, true);
+ int r = path_traverse(mdr, cf, mdr->get_filepath(),
+ MDS_TRAVERSE_DISCOVER | MDS_TRAVERSE_RDLOCK_PATH,
+ nullptr, &in);
+ if (r > 0)
+ return;
+ if (r < 0) {
+ mds->server->respond_to_request(mdr, r);
return;
+ }
- // TODO: Remove this restriction
- ceph_assert(in->is_auth());
+ // Cannot scrub same dentry twice at same time
+ if (in->scrub_is_in_progress()) {
+ mds->server->respond_to_request(mdr, -EBUSY);
+ return;
+ } else {
+ in->scrub_info();
+ }
C_MDS_EnqueueScrub *cs = static_cast<C_MDS_EnqueueScrub*>(mdr->internal_op_finish);
- ScrubHeaderRef header = cs->header;
- header->set_origin(in);
+ ScrubHeaderRef& header = cs->header;
- int r = mds->scrubstack->enqueue(in, header, !header->get_recursive());
+ r = mds->scrubstack->enqueue(in, header, !header->get_recursive());
mds->server->respond_to_request(mdr, r);
- return;
}
struct C_MDC_RespondInternalRequest : public MDCacheLogContext {
}
};
+struct C_MDC_ScrubRepaired : public MDCacheContext {
+ ScrubHeaderRef header;
+public:
+ C_MDC_ScrubRepaired(MDCache *m, const ScrubHeaderRef& h)
+ : MDCacheContext(m), header(h) {
+ header->inc_num_pending();
+ }
+ void finish(int r) override {
+ header->dec_num_pending();
+ }
+};
+
void MDCache::repair_dirfrag_stats(CDir *dir)
{
MDRequestRef mdr = request_start_internal(CEPH_MDS_OP_REPAIR_FRAGSTATS);
mdr->pin(dir);
mdr->internal_op_private = dir;
- mdr->internal_op_finish = new C_MDSInternalNoop;
+ if (dir->scrub_is_in_progress())
+ mdr->internal_op_finish = new C_MDC_ScrubRepaired(this, dir->get_scrub_header());
+ else
+ mdr->internal_op_finish = new C_MDSInternalNoop;
repair_dirfrag_stats_work(mdr);
}
MDRequestRef mdr = request_start_internal(CEPH_MDS_OP_REPAIR_INODESTATS);
mdr->auth_pin(diri); // already auth pinned by CInode::validate_disk_state()
mdr->internal_op_private = diri;
- mdr->internal_op_finish = new C_MDSInternalNoop;
+ if (diri->scrub_is_in_progress())
+ mdr->internal_op_finish = new C_MDC_ScrubRepaired(this, diri->get_scrub_header());
+ else
+ mdr->internal_op_finish = new C_MDSInternalNoop;
repair_inode_stats_work(mdr);
}
MDCache *cache;
MDRequestRef mdr;
public:
- C_MDS_RetryRequest(MDCache *c, MDRequestRef& r);
+ C_MDS_RetryRequest(MDCache *c, MDRequestRef& r) :
+ MDSInternalContext(c->mds), cache(c), mdr(r) {}
void finish(int r) override;
};
+class CF_MDS_RetryRequestFactory : public MDSContextFactory {
+public:
+ CF_MDS_RetryRequestFactory(MDCache *cache, MDRequestRef &mdr, bool dl) :
+ mdcache(cache), mdr(mdr), drop_locks(dl) {}
+ MDSContext *build() override;
+private:
+ MDCache *mdcache;
+ MDRequestRef mdr;
+ bool drop_locks;
+};
+
#endif
set_mdsmap_multimds_snaps_allowed();
}
}
+
+ if (whoami == 0)
+ scrubstack->advance_scrub_status();
}
if (is_active() || is_stopping()) {
type == MSG_MDS_LOCK ||
type == MSG_MDS_INODEFILECAPS ||
type == MSG_MDS_SCRUB ||
+ type == MSG_MDS_SCRUB_STATS ||
type == CEPH_MSG_CLIENT_CAPS ||
type == CEPH_MSG_CLIENT_CAPRELEASE ||
type == CEPH_MSG_CLIENT_LEASE) {
break;
case MSG_MDS_SCRUB:
+ case MSG_MDS_SCRUB_STATS:
ALLOW_MESSAGES_FROM(CEPH_ENTITY_TYPE_MDS);
scrubstack->dispatch(m);
break;
r = config_client(client_id, !got_value, option, value, *css);
} else if (command == "scrub start" ||
command == "scrub_start") {
+ if (whoami != 0) {
+ *css << "Not rank 0";
+ r = -EXDEV;
+ goto out;
+ }
+
string path;
string tag;
vector<string> scrubop_vec;
} else if (command == "scrub status") {
command_scrub_status(f);
} else if (command == "tag path") {
+ if (whoami != 0) {
+ *css << "Not rank 0";
+ r = -EXDEV;
+ goto out;
+ }
string path;
cmd_getval(cmdmap, "path", path);
string tag;
// Set after construction because it won't be known until we've
// started resolving path and locking
- void set_origin(CInode *origin_) { origin = origin_; }
+ void set_origin(inodeno_t ino) { origin = ino; }
bool get_recursive() const { return recursive; }
bool get_repair() const { return repair; }
bool get_force() const { return force; }
bool is_internal_tag() const { return is_tag_internal; }
- CInode *get_origin() const { return origin; }
+ inodeno_t get_origin() const { return origin; }
const std::string& get_tag() const { return tag; }
bool get_repaired() const { return repaired; }
void set_repaired() { repaired = true; }
+ void set_epoch_last_forwarded(unsigned epoch) { epoch_last_forwarded = epoch; }
+ unsigned get_epoch_last_forwarded() const { return epoch_last_forwarded; }
+
+ void inc_num_pending() { ++num_pending; }
+ void dec_num_pending() {
+ ceph_assert(num_pending > 0);
+ --num_pending;
+ }
+ unsigned get_num_pending() const { return num_pending; }
+
protected:
const std::string tag;
bool is_tag_internal;
const bool force;
const bool recursive;
const bool repair;
- CInode *origin = nullptr;
+ inodeno_t origin;
bool repaired = false; // May be set during scrub if repairs happened
+ unsigned epoch_last_forwarded = 0;
+ unsigned num_pending = 0;
};
typedef std::shared_ptr<ScrubHeader> ScrubHeaderRef;
scrub_stack.push_front(&obj->item_scrub);
else
scrub_stack.push_back(&obj->item_scrub);
+ return 0;
}
-void ScrubStack::enqueue(CInode *in, ScrubHeaderRef& header, bool top)
+int ScrubStack::enqueue(CInode *in, ScrubHeaderRef& header, bool top)
{
// abort in progress
if (clear_stack)
return -EAGAIN;
- scrub_origins.emplace(in);
- clog_scrub_summary(in);
+ header->set_origin(in->ino());
+ auto ret = scrubbing_map.emplace(header->get_tag(), header);
+ if (!ret.second) {
+ dout(10) << __func__ << " with {" << *in << "}"
+ << ", conflicting tag " << header->get_tag() << dendl;
+ return -EEXIST;
+ }
int r = _enqueue(in, header, top);
if (r < 0)
return r;
+ clog_scrub_summary(in);
+
kick_off_scrubs();
return 0;
}
dout(10) << __func__ << " forward to mds." << auth << dendl;
auto r = make_message<MMDSScrub>(MMDSScrub::OP_QUEUEINO, in->ino(),
std::move(in->scrub_queued_frags()),
- header->get_tag(), header->is_internal_tag(),
- header->get_force(), header->get_recursive(),
- header->get_repair());
+ header->get_tag(), header->get_origin(),
+ header->is_internal_tag(), header->get_force(),
+ header->get_recursive(), header->get_repair());
mdcache->mds->send_message_mds(r, auth);
scrub_r.gather_set.insert(auth);
dout(20) << __func__ << " forward " << p.second << " to mds." << p.first << dendl;
auto r = make_message<MMDSScrub>(MMDSScrub::OP_QUEUEDIR, in->ino(),
std::move(p.second), header->get_tag(),
- header->is_internal_tag(), header->get_force(),
- header->get_recursive(), header->get_repair());
+ header->get_origin(), header->is_internal_tag(),
+ header->get_force(), header->get_recursive(),
+ header->get_repair());
mds->send_message_mds(r, p.first);
scrub_r.gather_set.insert(p.first);
}
dout(10) << __func__ << " scrub passed on inode " << *in << dendl;
}
- if (in == header->get_origin()) {
- scrub_origins.erase(in);
- clog_scrub_summary(in);
- }
-
in->scrub_finished();
}
CachedStackStringStream cs;
if (state == STATE_IDLE) {
- return "idle";
+ if (scrubbing_map.empty())
+ return "idle";
+ *cs << "idle+waiting";
}
if (state == STATE_RUNNING) {
}
}
- if (!scrub_origins.empty()) {
- *cs << " [paths:";
- for (auto inode = scrub_origins.begin(); inode != scrub_origins.end(); ++inode) {
- if (inode != scrub_origins.begin()) {
- *cs << ",";
- }
-
- *cs << scrub_inode_path(*inode);
+ if (!scrubbing_map.empty()) {
+ *cs << " paths [";
+ bool first = true;
+ for (auto &p : scrubbing_map) {
+ if (!first)
+ *cs << ",";
+ auto& header = p.second;
+ if (CInode *in = mdcache->get_inode(header->get_origin()))
+ *cs << scrub_inode_path(in);
+ else
+ *cs << "#" << header->get_origin();
+ first = false;
}
*cs << "]";
}
bool have_more = false;
if (state == STATE_IDLE) {
- *css << "no active scrubs running";
+ if (scrubbing_map.empty())
+ *css << "no active scrubs running";
+ else
+ *css << state << " (waiting for more scrubs)";
} else if (state == STATE_RUNNING) {
if (clear_stack) {
*css << "ABORTING";
f->dump_string("status", css->strv());
f->open_object_section("scrubs");
- for (auto &inode : scrub_origins) {
+
+ for (auto& p : scrubbing_map) {
have_more = false;
- ScrubHeaderRefConst header = inode->get_scrub_header();
+ auto& header = p.second;
std::string tag(header->get_tag());
f->open_object_section(tag.c_str()); // scrub id
- f->dump_string("path", scrub_inode_path(inode));
+ if (CInode *in = mdcache->get_inode(header->get_origin()))
+ f->dump_string("path", scrub_inode_path(in));
+ else
+ f->dump_stream("path") << "#" << header->get_origin();
+
+ f->dump_string("tag", header->get_tag());
CachedStackStringStream optcss;
if (header->get_recursive()) {
auto abort_one = [this](MDSCacheObject *obj) {
if (CInode *in = dynamic_cast<CInode*>(obj)) {
- if (in == in->scrub_info()->header->get_origin()) {
- scrub_origins.erase(in);
- clog_scrub_summary(in);
- }
in->scrub_aborted();
} else if (CDir *dir = dynamic_cast<CDir*>(obj)) {
dir->scrub_aborted();
std::string what;
if (clear_stack) {
what = "aborted";
- } else if (scrub_origins.count(in)) {
+ } else if (in->scrub_is_in_progress()) {
what = "queued";
} else {
what = "completed";
handle_scrub(ref_cast<MMDSScrub>(m));
break;
+ case MSG_MDS_SCRUB_STATS:
+ handle_scrub_stats(ref_cast<MMDSScrubStats>(m));
+ break;
+
default:
derr << " scrub stack unknown message " << m->get_type() << dendl_impl;
ceph_abort_msg("scrub stack unknown message");
fragset_t queued;
if (!dfs.empty()) {
- ScrubHeaderRef header = std::make_shared<ScrubHeader>(m->get_tag(), m->is_internal_tag(),
- m->is_force(), m->is_recursive(),
- m->is_repair());
+ ScrubHeaderRef header;
+ if (auto it = scrubbing_map.find(m->get_tag()); it != scrubbing_map.end()) {
+ header = it->second;
+ } else {
+ header = std::make_shared<ScrubHeader>(m->get_tag(), m->is_internal_tag(),
+ m->is_force(), m->is_recursive(),
+ m->is_repair());
+ header->set_origin(m->get_origin());
+ scrubbing_map.emplace(header->get_tag(), header);
+ }
for (auto dir : dfs) {
queued.insert_raw(dir->get_frag());
- _enqueue(dir, header, nullptr, true);
+ _enqueue(dir, header, true);
}
queued.simplify();
kick_off_scrubs();
if (it->second.gather_set.empty()) {
remote_scrubs.erase(it);
+
+ const auto& header = diri->get_scrub_header();
+ header->set_epoch_last_forwarded(scrub_epoch);
remove_from_waiting(diri);
}
}
CInode *in = mdcache->get_inode(m->get_ino());
ceph_assert(in);
- ScrubHeaderRef header = std::make_shared<ScrubHeader>(m->get_tag(), m->is_internal_tag(),
- m->is_force(), m->is_recursive(),
- m->is_repair());
+ ScrubHeaderRef header;
+ if (auto it = scrubbing_map.find(m->get_tag()); it != scrubbing_map.end()) {
+ header = it->second;
+ } else {
+ header = std::make_shared<ScrubHeader>(m->get_tag(), m->is_internal_tag(),
+ m->is_force(), m->is_recursive(),
+ m->is_repair());
+ header->set_origin(m->get_origin());
+ scrubbing_map.emplace(header->get_tag(), header);
+ }
_enqueue(in, header, true);
in->scrub_queued_frags() = m->get_frags();
remove_from_waiting(in, false);
dequeue(in);
- if (in == in->scrub_info()->header->get_origin()) {
- scrub_origins.erase(in);
- clog_scrub_summary(in);
- }
+ const auto& header = in->get_scrub_header();
+ header->set_epoch_last_forwarded(scrub_epoch);
in->scrub_finished();
kick_off_scrubs();
}
}
+void ScrubStack::handle_scrub_stats(const cref_t<MMDSScrubStats> &m)
+{
+ mds_rank_t from = mds_rank_t(m->get_source().num());
+ dout(7) << __func__ << " " << *m << " from mds." << from << dendl;
+
+ if (from == 0) {
+ if (scrub_epoch != m->get_epoch() - 1) {
+ scrub_epoch = m->get_epoch() - 1;
+ for (auto& p : scrubbing_map) {
+ if (p.second->get_epoch_last_forwarded())
+ p.second->set_epoch_last_forwarded(scrub_epoch);
+ }
+ }
+ bool any_finished = false;
+ bool any_repaired = false;
+ std::set<std::string> scrubbing_tags;
+ for (auto it = scrubbing_map.begin(); it != scrubbing_map.end(); ) {
+ auto& header = it->second;
+ if (header->get_num_pending() ||
+ header->get_epoch_last_forwarded() >= scrub_epoch) {
+ scrubbing_tags.insert(it->first);
+ ++it;
+ } else if (m->is_finished(it->first)) {
+ any_finished = true;
+ if (header->get_repaired())
+ any_repaired = true;
+ scrubbing_map.erase(it++);
+ } else {
+ ++it;
+ }
+ }
+
+ scrub_epoch = m->get_epoch();
+
+ auto ack = make_message<MMDSScrubStats>(scrub_epoch, std::move(scrubbing_tags));
+ mdcache->mds->send_message_mds(ack, 0);
+
+ if (any_finished)
+ clog_scrub_summary();
+ if (any_repaired)
+ mdcache->mds->mdlog->trim_all();
+ } else {
+ if (scrub_epoch == m->get_epoch() &&
+ (size_t)from < mds_scrub_stats.size()) {
+ auto& stat = mds_scrub_stats[from];
+ stat.epoch_acked = m->get_epoch();
+ stat.scrubbing_tags = m->get_scrubbing_tags();
+ }
+ }
+}
+
+void ScrubStack::advance_scrub_status()
+{
+ if (scrubbing_map.empty())
+ return;
+
+ MDSRank *mds = mdcache->mds;
+
+ set<mds_rank_t> up_mds;
+ mds->get_mds_map()->get_up_mds_set(up_mds);
+ auto up_max = *up_mds.rbegin();
+
+ bool update_scrubbing = false;
+ std::set<std::string> scrubbing_tags;
+
+ if (up_max == 0) {
+ update_scrubbing = true;
+ } else if (mds_scrub_stats.size() > (size_t)(up_max)) {
+ bool fully_acked = true;
+ for (const auto& stat : mds_scrub_stats) {
+ if (stat.epoch_acked != scrub_epoch) {
+ fully_acked = false;
+ break;
+ }
+ scrubbing_tags.insert(stat.scrubbing_tags.begin(),
+ stat.scrubbing_tags.end());
+ }
+ if (fully_acked) {
+ // handle_scrub_stats() reports scrub is still in-progress if it has
+ // forwarded any object to other mds since previous epoch. Let's assume,
+ // at time 'A', we got scrub stats from all mds for previous epoch. If
+ // a scrub is not reported by any mds, we know there is no forward of
+ // the scrub since time 'A'. So we can consider the scrub is finished.
+ if (scrub_epoch_fully_acked + 1 == scrub_epoch)
+ update_scrubbing = true;
+ scrub_epoch_fully_acked = scrub_epoch;
+ }
+ }
+
+ if (mds_scrub_stats.size() != (size_t)up_max + 1)
+ mds_scrub_stats.resize((size_t)up_max + 1);
+ mds_scrub_stats.at(0).epoch_acked = scrub_epoch + 1;
+
+ bool any_finished = false;
+ bool any_repaired = false;
+
+ for (auto it = scrubbing_map.begin(); it != scrubbing_map.end(); ) {
+ auto& header = it->second;
+ if (header->get_num_pending() ||
+ header->get_epoch_last_forwarded() >= scrub_epoch) {
+ if (update_scrubbing && up_max != 0)
+ scrubbing_tags.insert(it->first);
+ ++it;
+ } else if (update_scrubbing && !scrubbing_tags.count(it->first)) {
+ // no longer being scrubbed globally
+ any_finished = true;
+ if (header->get_repaired())
+ any_repaired = true;
+ scrubbing_map.erase(it++);
+ } else {
+ ++it;
+ }
+ }
+
+ ++scrub_epoch;
+
+ for (auto& r : up_mds) {
+ if (r == 0)
+ continue;
+ auto m = update_scrubbing ?
+ make_message<MMDSScrubStats>(scrub_epoch, scrubbing_tags) :
+ make_message<MMDSScrubStats>(scrub_epoch);
+ mds->send_message_mds(m, r);
+ }
+
+ if (any_finished)
+ clog_scrub_summary();
+ if (any_repaired)
+ mdcache->mds->mdlog->trim_all();
+}
+
void ScrubStack::handle_mds_failure(mds_rank_t mds)
{
bool kick = false;
#include "common/LogClient.h"
#include "include/elist.h"
#include "messages/MMDSScrub.h"
+#include "messages/MMDSScrubStats.h"
class MDCache;
class Finisher;
* @param in The inode to scrub
* @param header The ScrubHeader propagated from wherever this scrub
*/
- void enqueue(CInode *in, ScrubHeaderRef& header, bool top);
+ int enqueue(CInode *in, ScrubHeaderRef& header, bool top);
/**
* Abort an ongoing scrub operation. The abort operation could be
* delayed if there are in-progress scrub operations on going. The
bool is_scrubbing() const { return !scrub_stack.empty(); }
+ void advance_scrub_status();
+
void handle_mds_failure(mds_rank_t mds);
void dispatch(const cref_t<Message> &m);
};
std::map<CInode*, scrub_remote_t> remote_scrubs;
+ unsigned scrub_epoch = 2;
+ unsigned scrub_epoch_fully_acked = 0;
+
+ struct scrub_stat_t {
+ unsigned epoch_acked = 0;
+ std::set<std::string> scrubbing_tags;
+ };
+ std::vector<scrub_stat_t> mds_scrub_stats;
+
+ std::map<std::string, ScrubHeaderRef> scrubbing_map;
+
friend class C_RetryScrub;
private:
// scrub abort is _not_ a state, rather it's an operation that's
void clog_scrub_summary(CInode *in=nullptr);
void handle_scrub(const cref_t<MMDSScrub> &m);
+ void handle_scrub_stats(const cref_t<MMDSScrubStats> &m);
State state = STATE_IDLE;
bool clear_stack = false;
// list of pending context completions for asynchronous scrub
// control operations.
std::vector<Context *> control_ctxs;
-
- // list of inodes for which scrub operations are running -- used
- // to diplay out in `scrub status`.
- std::set<CInode *> scrub_origins;
};
#endif /* SCRUBSTACK_H_ */
}
};
-class CF_MDS_MDRContextFactory : public MDSContextFactory {
-public:
- CF_MDS_MDRContextFactory(MDCache *cache, MDRequestRef &mdr, bool dl) :
- mdcache(cache), mdr(mdr), drop_locks(dl) {}
- MDSContext *build() {
- if (drop_locks) {
- mdcache->mds->locker->drop_locks(mdr.get(), nullptr);
- mdr->drop_local_auth_pins();
- }
- return new C_MDS_RetryRequest(mdcache, mdr);
- }
-private:
- MDCache *mdcache;
- MDRequestRef mdr;
- bool drop_locks;
-};
-
/* If this returns null, the request has been handled
* as appropriate: forwarded on, or the client's been replied to */
CInode* Server::rdlock_path_pin_ref(MDRequestRef& mdr,
return mdr->in[0];
// traverse
- CF_MDS_MDRContextFactory cf(mdcache, mdr, true);
+ CF_MDS_RetryRequestFactory cf(mdcache, mdr, true);
int flags = 0;
if (refpath.is_last_snap()) {
if (!no_want_auth)
}
// traverse to parent dir
- CF_MDS_MDRContextFactory cf(mdcache, mdr, true);
+ CF_MDS_RetryRequestFactory cf(mdcache, mdr, true);
int flags = MDS_TRAVERSE_RDLOCK_SNAP | MDS_TRAVERSE_RDLOCK_PATH |
MDS_TRAVERSE_WANT_DENTRY | MDS_TRAVERSE_XLOCK_DENTRY |
MDS_TRAVERSE_WANT_AUTH;
}
// traverse to parent dir
- CF_MDS_MDRContextFactory cf(mdcache, mdr, true);
+ CF_MDS_RetryRequestFactory cf(mdcache, mdr, true);
int flags = MDS_TRAVERSE_RDLOCK_SNAP | MDS_TRAVERSE_WANT_DENTRY | MDS_TRAVERSE_WANT_AUTH;
int r = mdcache->path_traverse(mdr, cf, refpath, flags, &mdr->dn[0]);
if (r != 0) {
want_auth = true; // set want_auth for CEPH_STAT_RSTAT mask
if (!mdr->is_batch_head() && mdr->can_batch()) {
- CF_MDS_MDRContextFactory cf(mdcache, mdr, false);
+ CF_MDS_RetryRequestFactory cf(mdcache, mdr, false);
int r = mdcache->path_traverse(mdr, cf, mdr->get_filepath(),
(want_auth ? MDS_TRAVERSE_WANT_AUTH : 0),
&mdr->dn[0], &mdr->in[0]);
filepath srcpath(mdr->peer_request->srcdnpath);
dout(10) << " src " << srcpath << dendl;
CInode *in;
- CF_MDS_MDRContextFactory cf(mdcache, mdr, false);
+ CF_MDS_RetryRequestFactory cf(mdcache, mdr, false);
int r = mdcache->path_traverse(mdr, cf, srcpath,
MDS_TRAVERSE_DISCOVER | MDS_TRAVERSE_PATH_LOCKED,
&trace, &in);
filepath destpath(mdr->peer_request->destdnpath);
dout(10) << " dest " << destpath << dendl;
vector<CDentry*> trace;
- CF_MDS_MDRContextFactory cf(mdcache, mdr, false);
+ CF_MDS_RetryRequestFactory cf(mdcache, mdr, false);
int r = mdcache->path_traverse(mdr, cf, destpath,
MDS_TRAVERSE_DISCOVER | MDS_TRAVERSE_PATH_LOCKED | MDS_TRAVERSE_WANT_DENTRY,
&trace);
encode(ino, payload);
encode(frags, payload);
encode(tag, payload);
+ encode(origin, payload);
encode(flags, payload);
}
void decode_payload() override {
decode(ino, p);
decode(frags, p);
decode(tag, p);
+ decode(origin, p);
decode(flags, p);
}
inodeno_t get_ino() const {
const std::string& get_tag() const {
return tag;
}
+ inodeno_t get_origin() const {
+ return origin;
+ }
int get_op() const {
return op;
}
static constexpr int HEAD_VERSION = 1;
static constexpr int COMPAT_VERSION = 1;
- MMDSScrub() : SafeMessage(MSG_MDS_SCRUB, HEAD_VERSION, COMPAT_VERSION) {}
+ MMDSScrub() : MMDSOp(MSG_MDS_SCRUB, HEAD_VERSION, COMPAT_VERSION) {}
MMDSScrub(int o, inodeno_t i, fragset_t&& _frags, std::string_view _tag,
- bool internal_tag=false, bool force=false,
- bool recursive=false, bool repair=false)
- : SafeMessage(MSG_MDS_SCRUB, HEAD_VERSION, COMPAT_VERSION),
- op(o), ino(i), frags(std::move(_frags)), tag(_tag) {
+ inodeno_t _origin=inodeno_t(), bool internal_tag=false,
+ bool force=false, bool recursive=false, bool repair=false)
+ : MMDSOp(MSG_MDS_SCRUB, HEAD_VERSION, COMPAT_VERSION),
+ op(o), ino(i), frags(std::move(_frags)), tag(_tag), origin(_origin) {
if (internal_tag) flags |= FLAG_INTERNAL_TAG;
if (force) flags |= FLAG_FORCE;
if (recursive) flags |= FLAG_RECURSIVE;
inodeno_t ino;
fragset_t frags;
std::string tag;
+ inodeno_t origin;
unsigned flags = 0;
};
#endif // CEPH_MMDSSCRUB_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_MMDSSCRUBSTATS_H
+#define CEPH_MMDSSCRUBSTATS_H
+
+#include "messages/MMDSOp.h"
+
+class MMDSScrubStats : public MMDSOp {
+ static constexpr int HEAD_VERSION = 1;
+ static constexpr int COMPAT_VERSION = 1;
+
+public:
+ std::string_view get_type_name() const override { return "mds_scrub_stats"; }
+ void print(ostream& o) const override {
+ o << "mds_scrub_stats(e" << epoch;
+ if (update_scrubbing)
+ o << " [" << scrubbing_tags << "])";
+ else
+ o << ")";
+ }
+
+ unsigned get_epoch() const { return epoch; }
+ const auto& get_scrubbing_tags() const { return scrubbing_tags; }
+ bool is_finished(const std::string& tag) const {
+ return update_scrubbing && !scrubbing_tags.count(tag);
+ }
+
+ void encode_payload(uint64_t features) override {
+ using ceph::encode;
+ encode(epoch, payload);
+ encode(scrubbing_tags, payload);
+ encode(update_scrubbing, payload);
+ }
+ void decode_payload() override {
+ using ceph::decode;
+ auto p = payload.cbegin();
+ decode(epoch, p);
+ decode(scrubbing_tags, p);
+ decode(update_scrubbing, p);
+ }
+
+protected:
+ MMDSScrubStats(unsigned e=0) :
+ MMDSOp(MSG_MDS_SCRUB_STATS, HEAD_VERSION, COMPAT_VERSION),
+ epoch(e) {}
+ MMDSScrubStats(unsigned e, std::set<std::string>&& tags) :
+ MMDSOp(MSG_MDS_SCRUB_STATS, HEAD_VERSION, COMPAT_VERSION),
+ epoch(e), scrubbing_tags(std::move(tags)), update_scrubbing(true) {}
+ MMDSScrubStats(unsigned e, const std::set<std::string>& tags) :
+ MMDSOp(MSG_MDS_SCRUB_STATS, HEAD_VERSION, COMPAT_VERSION),
+ epoch(e), scrubbing_tags(tags), update_scrubbing(true) {}
+ ~MMDSScrubStats() override {}
+
+private:
+ unsigned epoch;
+ std::set<std::string> scrubbing_tags;
+ bool update_scrubbing = false;
+
+ template<class T, typename... Args>
+ friend boost::intrusive_ptr<T> ceph::make_message(Args&&... args);
+};
+
+#endif
#include "messages/MMDSOpenInoReply.h"
#include "messages/MMDSSnapUpdate.h"
#include "messages/MMDSScrub.h"
+#include "messages/MMDSScrubStats.h"
#include "messages/MDirUpdate.h"
#include "messages/MDiscover.h"
m = make_message<MMDSScrub>();
break;
+ case MSG_MDS_SCRUB_STATS:
+ m = make_message<MMDSScrubStats>();
+ break;
+
case MSG_MDS_EXPORTDIRDISCOVER:
m = make_message<MExportDirDiscover>();
break;
#define MSG_MDS_HEARTBEAT 0x500 // for mds load balancer
#define MSG_MDS_METRICS 0x501 // for mds metric aggregator
#define MSG_MDS_PING 0x502 // for mds pinger
+#define MSG_MDS_SCRUB_STATS 0x503 // for mds scrub stack
// *** generic ***
#define MSG_TIMECHECK 0x600