me->scrub_infop.swap(si);
}
-void CDir::scrub_initialize(const ScrubHeaderRef& header, MDSContext* f)
+void CDir::scrub_initialize(const ScrubHeaderRef& header)
{
ceph_assert(header);
// FIXME: weird implicit construction, is someone else meant
// to be calling scrub_info_create first?
scrub_info();
scrub_infop->header = header;
- scrub_infop->on_finish = f;
scrub_infop->directory_scrubbing = true;
}
-void CDir::scrub_aborted(MDSContext **c) {
+void CDir::scrub_aborted() {
dout(20) << __func__ << dendl;
ceph_assert(scrub_is_in_progress());
- *c = scrub_infop->on_finish;
- scrub_infop->on_finish = nullptr;
-
scrub_infop->directory_scrubbing = false;
scrub_infop->last_scrub_dirty = false;
scrub_infop.reset();
}
-void CDir::scrub_finished(MDSContext **c)
+void CDir::scrub_finished()
{
dout(20) << __func__ << dendl;
ceph_assert(scrub_is_in_progress());
scrub_infop->last_recursive = scrub_infop->last_local;
scrub_infop->last_scrub_dirty = true;
-
- *c = scrub_infop->on_finish;
- scrub_infop->on_finish = nullptr;
}
void CDir::scrub_maybe_delete_info()
scrub_info_t() {}
- MDSContext *on_finish = nullptr;
-
scrub_stamps last_recursive; // when we last finished a recursive scrub
scrub_stamps last_local; // when we last did a local scrub
* @pre The CDir is marked complete.
* @post It has set up its internal scrubbing state.
*/
- void scrub_initialize(const ScrubHeaderRef& header, MDSContext* f);
+ void scrub_initialize(const ScrubHeaderRef& header);
ScrubHeaderRef get_scrub_header() {
return scrub_infop ? scrub_infop->header : nullptr;
}
* Call this once all CDentries have been scrubbed, according to
* scrub_dentry_next's listing. It finalizes the scrub statistics.
*/
- void scrub_finished(MDSContext **c);
+ void scrub_finished();
- void scrub_aborted(MDSContext **c);
+ void scrub_aborted();
/**
* Tell the CDir to do a local scrub of itself.
* @pre The CDir is_complete().
}
}
-void CInode::scrub_initialize(ScrubHeaderRef& header,
- MDSContext *f)
+void CInode::scrub_initialize(ScrubHeaderRef& header)
{
dout(20) << __func__ << " with scrub_version " << get_version() << dendl;
scrub_info();
- scrub_infop->on_finish = f;
scrub_infop->scrub_in_progress = true;
scrub_infop->queued_frags.clear();
scrub_infop->header = header;
// right now we don't handle remote inodes
}
-void CInode::scrub_aborted(MDSContext **c) {
+void CInode::scrub_aborted() {
dout(20) << __func__ << dendl;
ceph_assert(scrub_is_in_progress());
- *c = nullptr;
- std::swap(*c, scrub_infop->on_finish);
-
scrub_infop->scrub_in_progress = false;
scrub_maybe_delete_info();
}
-void CInode::scrub_finished(MDSContext **c) {
+void CInode::scrub_finished() {
dout(20) << __func__ << dendl;
ceph_assert(scrub_is_in_progress());
scrub_infop->last_scrub_stamp = ceph_clock_now();
scrub_infop->last_scrub_dirty = true;
scrub_infop->scrub_in_progress = false;
-
- *c = scrub_infop->on_finish;
- scrub_infop->on_finish = NULL;
-
- if (scrub_infop->header->get_origin() == this) {
- // We are at the point that a tagging scrub was initiated
- LogChannelRef clog = mdcache->mds->clog;
- clog->info() << "scrub complete with tag '"
- << scrub_infop->header->get_tag() << "'";
- }
}
int64_t CInode::get_backtrace_pool() const
public:
scrub_info_t() {}
- MDSContext *on_finish = nullptr;
-
version_t last_scrub_version = 0;
utime_t last_scrub_stamp;
* @param scrub_version What version are we scrubbing at (usually, parent
* directory's get_projected_version())
*/
- void scrub_initialize(ScrubHeaderRef& header, MDSContext *f);
+ void scrub_initialize(ScrubHeaderRef& header);
/**
* Call this once the scrub has been completed, whether it's a full
* recursive scrub on a directory or simply the data on a file (or
* @param c An out param which is filled in with a Context* that must
* be complete()ed.
*/
- void scrub_finished(MDSContext **c);
+ void scrub_finished();
- void scrub_aborted(MDSContext **c);
+ void scrub_aborted();
fragset_t& scrub_queued_frags() {
ceph_assert(scrub_infop);
return scrub_infop->queued_frags;
}
- void scrub_set_finisher(MDSContext *c) {
- ceph_assert(!scrub_infop->on_finish);
- scrub_infop->on_finish = c;
- }
-
bool is_multiversion() const {
return snaprealm || // other snaprealms will link to me
get_inode()->is_dir() || // links to me in other snaps
C_MDS_EnqueueScrub(std::string_view tag, Formatter *f, Context *fin) :
tag(tag), formatter(f), on_finish(fin), header(nullptr) {}
- Context *take_finisher() {
- Context *fin = on_finish;
- on_finish = NULL;
- return fin;
- }
-
void finish(int r) override {
if (r == 0) {
// since recursive scrub is asynchronous, dump minimal output
}
C_MDS_EnqueueScrub *cs = new C_MDS_EnqueueScrub(tag_str, f, fin);
- cs->header = std::make_shared<ScrubHeader>(
- tag_str, is_internal, force, recursive, repair, f);
+ cs->header = std::make_shared<ScrubHeader>(tag_str, is_internal, force, recursive, repair);
mdr->internal_op_finish = cs;
enqueue_scrub_work(mdr);
C_MDS_EnqueueScrub *cs = static_cast<C_MDS_EnqueueScrub*>(mdr->internal_op_finish);
ScrubHeaderRef header = cs->header;
-
- // 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();
- }
-
header->set_origin(in);
- Context *fin = nullptr;
- if (!header->get_recursive())
- fin = cs->take_finisher();
-
- // If the scrub did some repair, then flush the journal at the end of
- // the scrub. Otherwise in the case of e.g. rewriting a backtrace
- // the on disk state will still look damaged.
- auto scrub_finish = new LambdaContext([this, header, fin](int r){
- if (!header->get_repaired()) {
- if (fin)
- fin->complete(r);
- return;
- }
-
- auto flush_finish = new LambdaContext([this, fin](int r){
- dout(4) << "Expiring log segments because scrub did some repairs" << dendl;
- mds->mdlog->trim_all();
+ int r = mds->scrubstack->enqueue(in, header, !header->get_recursive());
- if (fin) {
- MDSGatherBuilder gather(g_ceph_context);
- auto& expiring_segments = mds->mdlog->get_expiring_segments();
- for (auto logseg : expiring_segments)
- logseg->wait_for_expiry(gather.new_sub());
- ceph_assert(gather.has_subs());
- gather.set_finisher(new MDSInternalContextWrapper(mds, fin));
- gather.activate();
- }
- });
-
- dout(4) << "Flushing journal because scrub did some repairs" << dendl;
- mds->mdlog->start_new_segment();
- mds->mdlog->flush();
- mds->mdlog->wait_for_safe(new MDSInternalContextWrapper(mds, flush_finish));
- });
-
- mds->scrubstack->enqueue(in, header,
- new MDSInternalContextWrapper(mds, scrub_finish),
- !header->get_recursive());
-
- mds->server->respond_to_request(mdr, 0);
+ mds->server->respond_to_request(mdr, r);
return;
}
class ScrubHeader {
public:
ScrubHeader(std::string_view tag_, bool is_tag_internal_, bool force_,
- bool recursive_, bool repair_, ceph::Formatter *f_)
+ bool recursive_, bool repair_)
: tag(tag_), is_tag_internal(is_tag_internal_), force(force_),
- recursive(recursive_), repair(repair_), formatter(f_) {}
+ recursive(recursive_), repair(repair_) {}
// Set after construction because it won't be known until we've
// started resolving path and locking
bool is_internal_tag() const { return is_tag_internal; }
CInode *get_origin() const { return origin; }
const std::string& get_tag() const { return tag; }
- ceph::Formatter& get_formatter() const { return *formatter; }
bool get_repaired() const { return repaired; }
void set_repaired() { repaired = true; }
const bool force;
const bool recursive;
const bool repair;
- ceph::Formatter* const formatter;
CInode *origin = nullptr;
bool repaired = false; // May be set during scrub if repairs happened
stack_size--;
}
-void ScrubStack::_enqueue(MDSCacheObject *obj, ScrubHeaderRef& header,
- MDSContext *on_finish, bool top)
+int ScrubStack::_enqueue(MDSCacheObject *obj, ScrubHeaderRef& header, bool top)
{
ceph_assert(ceph_mutex_is_locked_by_me(mdcache->mds->mds_lock));
if (CInode *in = dynamic_cast<CInode*>(obj)) {
- dout(10) << __func__ << " with {" << *in << "}"
- << ", on_finish=" << on_finish << ", top=" << top << dendl;
- in->scrub_initialize(header, on_finish);
+ if (in->scrub_is_in_progress()) {
+ dout(10) << __func__ << " with {" << *in << "}" << ", already in scrubbing" << dendl;
+ return -EBUSY;
+ }
+
+ dout(10) << __func__ << " with {" << *in << "}" << ", top=" << top << dendl;
+ in->scrub_initialize(header);
} else if (CDir *dir = dynamic_cast<CDir*>(obj)) {
- dout(10) << __func__ << " with {" << *dir << "}"
- << ", on_finish=" << on_finish << ", top=" << top << dendl;
+ if (dir->scrub_is_in_progress()) {
+ dout(10) << __func__ << " with {" << *dir << "}" << ", already in scrubbing" << dendl;
+ return -EBUSY;
+ }
+
+ dout(10) << __func__ << " with {" << *dir << "}" << ", top=" << top << dendl;
// The edge directory must be in memory
dir->auth_pin(this);
- dir->scrub_initialize(header, on_finish);
+ dir->scrub_initialize(header);
} else {
ceph_assert(0 == "queue dentry to scrub stack");
}
scrub_stack.push_back(&obj->item_scrub);
}
-void ScrubStack::enqueue(CInode *in, ScrubHeaderRef& header,
- MDSContext *on_finish, bool top)
+void ScrubStack::enqueue(CInode *in, ScrubHeaderRef& header, bool top)
{
// abort in progress
- if (clear_stack) {
- on_finish->complete(-EAGAIN);
- return;
- }
+ if (clear_stack)
+ return -EAGAIN;
scrub_origins.emplace(in);
clog_scrub_summary(in);
- _enqueue(in, header, on_finish, top);
+ int r = _enqueue(in, header, top);
+ if (r < 0)
+ return r;
+
kick_off_scrubs();
+ return 0;
}
void ScrubStack::add_to_waiting(MDSCacheObject *obj)
// it's a regular file, symlink, or hard link
dequeue(in); // we only touch it this once, so remove from stack
- if (!in->scrub_info()->on_finish) {
- scrubs_in_progress++;
- in->scrub_set_finisher(&scrub_kick);
- }
scrub_file_inode(in);
} else {
bool added_children = false;
dout(20) << __func__ << " barebones " << *dir << dendl;
dir->fetch(gather.new_sub());
} else {
- _enqueue(dir, header, nullptr, true);
+ _enqueue(dir, header, true);
queued.insert_raw(dir->get_frag());
*added_children = true;
}
class C_InodeValidated : public MDSInternalContext
{
- public:
- ScrubStack *stack;
- CInode::validated_data result;
- CInode *target;
-
- C_InodeValidated(MDSRank *mds, ScrubStack *stack_, CInode *target_)
- : MDSInternalContext(mds), stack(stack_), target(target_)
- {}
+public:
+ ScrubStack *stack;
+ CInode::validated_data result;
+ CInode *target;
- void finish(int r) override
- {
- stack->_validate_inode_done(target, r, result);
- }
+ C_InodeValidated(MDSRank *mds, ScrubStack *stack_, CInode *target_)
+ : MDSInternalContext(mds), stack(stack_), target(target_)
+ {
+ stack->scrubs_in_progress++;
+ }
+ void finish(int r) override {
+ stack->_validate_inode_done(target, r, result);
+ stack->scrubs_in_progress--;
+ stack->kick_off_scrubs();
+ }
};
void ScrubStack::scrub_dir_inode_final(CInode *in)
{
dout(20) << __func__ << " " << *in << dendl;
- if (!in->scrub_info()->on_finish) {
- scrubs_in_progress++;
- in->scrub_set_finisher(&scrub_kick);
- }
-
C_InodeValidated *fin = new C_InodeValidated(mdcache->mds, this, in);
in->validate_disk_state(&fin->result, fin);
-
return;
}
continue;
}
if (dnl->is_primary()) {
- _enqueue(dnl->get_inode(), header, nullptr, false);
+ _enqueue(dnl->get_inode(), header, false);
} else if (dnl->is_remote()) {
// TODO: check remote linkage
}
dir->scrub_local();
- MDSContext *c = nullptr;
- dir->scrub_finished(&c);
+ dir->scrub_finished();
dir->auth_unpin(this);
- if (c)
- finisher->queue(new MDSIOContextWrapper(mdcache->mds, c), 0);
*done = true;
dout(10) << __func__ << " done" << dendl;
dout(10) << __func__ << " scrub passed on inode " << *in << dendl;
}
- MDSContext *c = nullptr;
- in->scrub_finished(&c);
- if (c)
- finisher->queue(new MDSIOContextWrapper(mdcache->mds, c), 0);
-
if (in == header->get_origin()) {
scrub_origins.erase(in);
clog_scrub_summary(in);
- if (!header->get_recursive() && header->get_formatter()) {
- if (r >= 0) { // we got into the scrubbing dump it
- result.dump(header->get_formatter());
- } else { // we failed the lookup or something; dump ourselves
- header->get_formatter()->open_object_section("results");
- header->get_formatter()->dump_int("return_code", r);
- header->get_formatter()->close_section(); // results
- }
- }
}
-}
-ScrubStack::C_KickOffScrubs::C_KickOffScrubs(ScrubStack *s)
- : MDSInternalContext(s->mdcache->mds), stack(s) { }
+ in->scrub_finished();
+}
void ScrubStack::complete_control_contexts(int r) {
ceph_assert(ceph_mutex_is_locked_by_me(mdcache->mds->mds_lock));
scrub_origins.erase(in);
clog_scrub_summary(in);
}
- MDSContext *ctx = nullptr;
- in->scrub_aborted(&ctx);
- if (ctx != nullptr) {
- ctx->complete(-ECANCELED);
- }
+ in->scrub_aborted();
} else if (CDir *dir = dynamic_cast<CDir*>(obj)) {
- MDSContext *ctx = nullptr;
- dir->scrub_aborted(&ctx);
+ dir->scrub_aborted();
dir->auth_unpin(this);
- if (ctx != nullptr) {
- ctx->complete(-ECANCELED);
- }
} else {
ceph_abort(0 == "dentry in scrub stack");
}
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(), nullptr);
+ m->is_repair());
for (auto dir : dfs) {
queued.insert_raw(dir->get_frag());
_enqueue(dir, header, nullptr, true);
ScrubHeaderRef header = std::make_shared<ScrubHeader>(m->get_tag(), m->is_internal_tag(),
m->is_force(), m->is_recursive(),
- m->is_repair(), nullptr);
+ m->is_repair());
- _enqueue(in, header, nullptr, true);
+ _enqueue(in, header, true);
in->scrub_queued_frags() = m->get_frags();
kick_off_scrubs();
scrub_origins.erase(in);
clog_scrub_summary(in);
}
- MDSContext *c = nullptr;
- in->scrub_finished(&c);
- if (c)
- finisher->queue(new MDSIOContextWrapper(mdcache->mds, c), 0);
+ in->scrub_finished();
kick_off_scrubs();
}
clog(clog),
finisher(finisher_),
scrub_stack(member_offset(MDSCacheObject, item_scrub)),
- scrub_waiting(member_offset(MDSCacheObject, item_scrub)),
- scrub_kick(this) {}
+ scrub_waiting(member_offset(MDSCacheObject, item_scrub)) {}
~ScrubStack() {
ceph_assert(scrub_stack.empty());
ceph_assert(!scrubs_in_progress);
* @param in The inode to scrub
* @param header The ScrubHeader propagated from wherever this scrub
*/
- void enqueue(CInode *in, ScrubHeaderRef& header,
- MDSContext *on_finish, bool top);
+ void 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
};
std::map<CInode*, scrub_remote_t> remote_scrubs;
- class C_KickOffScrubs : public MDSInternalContext {
- public:
- C_KickOffScrubs(ScrubStack *s);
- void finish(int r) override { }
- void complete(int r) override {
- if (r == -ECANCELED) {
- return;
- }
-
- stack->scrubs_in_progress--;
- stack->kick_off_scrubs();
- // don't delete self
- }
- private:
- ScrubStack *stack;
- };
- C_KickOffScrubs scrub_kick;
-
friend class C_RetryScrub;
private:
// scrub abort is _not_ a state, rather it's an operation that's
friend class C_InodeValidated;
- void _enqueue(MDSCacheObject *obj, ScrubHeaderRef& header,
- MDSContext *on_finish, bool top);
+ int _enqueue(MDSCacheObject *obj, ScrubHeaderRef& header, bool top);
/**
* Remove the inode/dirfrag from the stack.
*/