]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mds: multiple mds scrub support
authorSimon Gao <simon29rock@gmail.com>
Thu, 28 May 2020 08:22:12 +0000 (16:22 +0800)
committerYan, Zheng <zyan@redhat.com>
Mon, 16 Nov 2020 01:02:17 +0000 (09:02 +0800)
If a non-auth object is encountered during scrubbing, forward scrub
to the object's auth mds.

Fixes: https://tracker.ceph.com/issues/12274
Signed-off-by: Simon Gao <simon29rock@gmail.com>
Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
src/include/frag.h
src/mds/CInode.cc
src/mds/CInode.h
src/mds/MDSRank.cc
src/mds/ScrubHeader.h
src/mds/ScrubStack.cc
src/mds/ScrubStack.h
src/messages/MMDSScrub.h [new file with mode: 0644]
src/msg/Message.cc
src/msg/Message.h

index a4e20bacab2246e5c194f6b38773af0b32f80592..ec18bddfbb1e761d3faa655161a4601b8fd11273 100644 (file)
@@ -570,6 +570,10 @@ public:
     }
   }
 
+  void clear() {
+    _set.clear();
+  }
+
   void insert_raw(frag_t f){
     _set.insert(f);
   }
index 63f6548b0ae9e941aac403a8b4538cd6c9490926..3e771e30429a01ab9756618b3c03223132e7dcb6 100644 (file)
@@ -5107,6 +5107,7 @@ void CInode::scrub_initialize(ScrubHeaderRef& header,
   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
 }
index e38286dbc6eaca18b5405ab80d97493c45e61d63..470fcfaec8e76d58040ff02da6b0cbe720d412c9 100644 (file)
@@ -306,6 +306,8 @@ class CInode : public MDSCacheObject, public InodeStoreBase, public Counter<CIno
     bool last_scrub_dirty = false; /// are our stamps dirty with respect to disk state?
     bool scrub_in_progress = false; /// are we currently scrubbing?
 
+    fragset_t queued_frags;
+
     ScrubHeaderRef header;
   };
 
@@ -422,7 +424,7 @@ class CInode : public MDSCacheObject, public InodeStoreBase, public Counter<CIno
 
   std::ostream& print_db_line_prefix(std::ostream& out) override;
 
-  const scrub_info_t *scrub_info() const{
+  const scrub_info_t *scrub_info() const {
     if (!scrub_infop)
       scrub_info_create();
     return scrub_infop.get();
@@ -455,6 +457,11 @@ class CInode : public MDSCacheObject, public InodeStoreBase, public Counter<CIno
 
   void scrub_aborted(MDSContext **c);
 
+  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;
index 80046d723a65035ec0ce8d8762ef40f94115da53..b6b06a06d61cb24ff1295d13110f9269993c6617 100644 (file)
@@ -1171,6 +1171,7 @@ bool MDSRank::is_valid_message(const cref_t<Message> &m) {
       type == MSG_MDS_TABLE_REQUEST ||
       type == MSG_MDS_LOCK ||
       type == MSG_MDS_INODEFILECAPS ||
+      type == MSG_MDS_SCRUB ||
       type == CEPH_MSG_CLIENT_CAPS ||
       type == CEPH_MSG_CLIENT_CAPRELEASE ||
       type == CEPH_MSG_CLIENT_LEASE) {
@@ -1256,6 +1257,11 @@ void MDSRank::handle_message(const cref_t<Message> &m)
       locker->dispatch(m);
       break;
 
+    case MSG_MDS_SCRUB:
+      ALLOW_MESSAGES_FROM(CEPH_ENTITY_TYPE_MDS);
+      scrubstack->dispatch(m);
+      break;
+
     default:
       derr << "unrecognized message " << *m << dendl;
     }
@@ -2467,13 +2473,8 @@ void MDSRankDispatcher::handle_mds_map(
   if (mdsmap->get_inline_data_enabled() && !oldmap.get_inline_data_enabled())
     dout(0) << "WARNING: inline_data support has been deprecated and will be removed in a future release" << dendl;
 
-  if (scrubstack->is_scrubbing()) {
-    if (mdsmap->get_max_mds() > 1) {
-      auto c = new C_MDSInternalNoop;
-      scrubstack->scrub_abort(c);
-    }
-  }
   mdcache->handle_mdsmap(*mdsmap, oldmap);
+
   if (metric_aggregator != nullptr) {
     metric_aggregator->notify_mdsmap(*mdsmap);
   }
@@ -2504,6 +2505,8 @@ void MDSRank::handle_mds_failure(mds_rank_t who)
     snapserver->handle_mds_failure_or_stop(who);
 
   snapclient->handle_mds_failure(who);
+
+  scrubstack->handle_mds_failure(who);
 }
 
 void MDSRankDispatcher::handle_asok_command(
@@ -2612,13 +2615,6 @@ void MDSRankDispatcher::handle_asok_command(
     cmd_getval(cmdmap, "path", path);
     cmd_getval(cmdmap, "tag", tag);
 
-    /* Multiple MDS scrub is not currently supported. See also: https://tracker.ceph.com/issues/12274 */
-    if (mdsmap->get_max_mds() > 1) {
-      *css << "Scrub is not currently supported for multiple active MDS. Please reduce max_mds to 1 and then scrub.";
-      r = -EINVAL;
-      goto out;
-    }
-
     finisher->queue(
       new LambdaContext(
        [this, on_finish, f, path, tag, scrubop_vec](int r) {
index a41471fec210cba6f562f73a187517db33e035f1..0925c28d6b853a753f8c48d486d135f3bd813e78 100644 (file)
@@ -37,10 +37,7 @@ public:
   ScrubHeader(std::string_view tag_, bool is_tag_internal_, bool force_,
               bool recursive_, bool repair_, ceph::Formatter *f_)
     : tag(tag_), is_tag_internal(is_tag_internal_), force(force_),
-      recursive(recursive_), repair(repair_), formatter(f_)
-  {
-    ceph_assert(formatter != nullptr);
-  }
+      recursive(recursive_), repair(repair_), formatter(f_) {}
 
   // Set after construction because it won't be known until we've
   // started resolving path and locking
@@ -51,7 +48,7 @@ public:
   bool get_force() const { return force; }
   bool is_internal_tag() const { return is_tag_internal; }
   CInode *get_origin() const { return origin; }
-  std::string_view get_tag() const { return tag; }
+  const std::string& get_tag() const { return tag; }
   ceph::Formatter& get_formatter() const { return *formatter; }
 
   bool get_repaired() const { return repaired; }
index c27d8d4a06c57cbea43f0593f97cacb35b2d20b3..f454f2334ca4fc684abb7251addd62a3f5fa9ae3 100644 (file)
@@ -110,13 +110,14 @@ void ScrubStack::add_to_waiting(MDSCacheObject *obj)
   scrub_waiting.push_back(&obj->item_scrub);
 }
 
-void ScrubStack::remove_from_waiting(MDSCacheObject *obj)
+void ScrubStack::remove_from_waiting(MDSCacheObject *obj, bool kick)
 {
   scrubs_in_progress--;
   if (obj->item_scrub.is_on_list()) {
     obj->item_scrub.remove_myself();
     scrub_stack.push_front(&obj->item_scrub);
-    kick_off_scrubs();
+    if (kick)
+      kick_off_scrubs();
   }
 }
 
@@ -175,10 +176,13 @@ void ScrubStack::kick_off_scrubs()
     assert(state == STATE_RUNNING || state == STATE_IDLE);
     set_state(STATE_RUNNING);
 
-
     if (CInode *in = dynamic_cast<CInode*>(*it)) {
       dout(20) << __func__ << " examining " << *in << dendl;
       ++it;
+
+      if (!validate_inode_auth(in))
+       continue;
+
       if (!in->is_dir()) {
        // it's a regular file, symlink, or hard link
        dequeue(in); // we only touch it this once, so remove from stack
@@ -219,47 +223,125 @@ void ScrubStack::kick_off_scrubs()
   }
 }
 
+bool ScrubStack::validate_inode_auth(CInode *in)
+{
+  if (in->is_auth()) {
+    if (!in->can_auth_pin()) {
+      dout(10) << __func__ << " can't auth pin" << dendl;
+      in->add_waiter(CInode::WAIT_UNFREEZE, new C_RetryScrub(this, in));
+      return false;
+    }
+    return true;
+  } else {
+    MDSRank *mds = mdcache->mds;
+    if (in->is_ambiguous_auth()) {
+      dout(10) << __func__ << " ambiguous auth" << dendl;
+      in->add_waiter(MDSCacheObject::WAIT_SINGLEAUTH, new C_RetryScrub(this, in));
+    } else if (mds->is_cluster_degraded()) {
+      dout(20) << __func__ << " cluster degraded" << dendl;
+      mds->wait_for_cluster_recovered(new C_RetryScrub(this, in));
+    } else {
+      ScrubHeaderRef header = in->get_scrub_header();
+      ceph_assert(header);
+
+      auto ret = remote_scrubs.emplace(std::piecewise_construct,
+                                      std::forward_as_tuple(in),
+                                      std::forward_as_tuple());
+      ceph_assert(ret.second); // FIXME: parallel scrubs?
+      auto &scrub_r = ret.first->second;
+      scrub_r.tag = header->get_tag();
+
+      mds_rank_t auth = in->authority().first;
+      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());
+      mdcache->mds->send_message_mds(r, auth);
+
+      scrub_r.gather_set.insert(auth);
+      // wait for ACK
+      add_to_waiting(in);
+    }
+    return false;
+  }
+}
+
 void ScrubStack::scrub_dir_inode(CInode *in, bool *added_children, bool *done)
 {
   dout(10) << __func__ << " " << *in << dendl;
+  ceph_assert(in->is_auth());
+  MDSRank *mds = mdcache->mds;
 
   ScrubHeaderRef header = in->get_scrub_header();
   ceph_assert(header);
 
   MDSGatherBuilder gather(g_ceph_context);
 
+  auto &queued = in->scrub_queued_frags();
+  std::map<mds_rank_t, fragset_t> scrub_remote;
+
   frag_vec_t frags;
   in->dirfragtree.get_leaves(frags);
   dout(20) << __func__ << "recursive mode, frags " << frags << dendl;
-
   for (auto &fg : frags) {
+    if (queued.contains(fg))
+      continue;
     CDir *dir = in->get_or_open_dirfrag(mdcache, fg);
     if (!dir->is_auth()) {
-      dout(20) << __func__ << " not auth " << *dir  << dendl;
-      // no-op
+      if (dir->is_ambiguous_auth()) {
+       dout(20) << __func__ << " ambiguous auth " << *dir  << dendl;
+       dir->add_waiter(MDSCacheObject::WAIT_SINGLEAUTH, gather.new_sub());
+      } else if (mds->is_cluster_degraded()) {
+       dout(20) << __func__ << " cluster degraded" << dendl;
+       mds->wait_for_cluster_recovered(gather.new_sub());
+      } else {
+       mds_rank_t auth = dir->authority().first;
+       scrub_remote[auth].insert_raw(fg);
+      }
     } else if (!dir->can_auth_pin()) {
       dout(20) << __func__ << " freezing/frozen " << *dir  << dendl;
       dir->add_waiter(CDir::WAIT_UNFREEZE, gather.new_sub());
     } else if (dir->get_version() == 0) {
       dout(20) << __func__ << " barebones " << *dir  << dendl;
       dir->fetch(gather.new_sub());
+    } else {
+      _enqueue(dir, header, nullptr, true);
+      queued.insert_raw(dir->get_frag());
+      *added_children = true;
     }
   }
+
+  queued.simplify();
+
   if (gather.has_subs()) {
     gather.set_finisher(new C_RetryScrub(this, in));
     gather.activate();
     return;
   }
 
-  std::vector<CDir*> dfs;
-  in->get_dirfrags(dfs);
-  for (auto &dir : dfs) {
-    if (dir->is_auth()){
-      _enqueue(dir, header, nullptr, true);
-      *added_children = true;
-    } else {
-      // FIXME: ask auth mds to scrub
+  if (!scrub_remote.empty()) {
+    auto ret = remote_scrubs.emplace(std::piecewise_construct,
+                                    std::forward_as_tuple(in),
+                                    std::forward_as_tuple());
+    ceph_assert(ret.second); // FIXME: parallel scrubs?
+    auto &scrub_r = ret.first->second;
+    scrub_r.tag = header->get_tag();
+
+    for (auto& p : scrub_remote) {
+      p.second.simplify();
+      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());
+      mds->send_message_mds(r, p.first);
+      scrub_r.gather_set.insert(p.first);
     }
+    // wait for ACKs
+    add_to_waiting(in);
+    return;
   }
 
   scrub_dir_inode_final(in);
@@ -415,13 +497,13 @@ void ScrubStack::_validate_inode_done(CInode *in, int r,
   if (in == header->get_origin()) {
     scrub_origins.erase(in);
     clog_scrub_summary(in);
-    if (!header->get_recursive()) {
+    if (!header->get_recursive() && header->get_formatter()) {
       if (r >= 0) { // we got into the scrubbing dump it
-        result.dump(&(header->get_formatter()));
+        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
+        header->get_formatter()->open_object_section("results");
+        header->get_formatter()->dump_int("return_code", r);
+        header->get_formatter()->close_section(); // results
       }
     }
   }
@@ -612,6 +694,11 @@ void ScrubStack::abort_pending_scrubs() {
   stack_size = 0;
   scrub_stack.clear();
   scrub_waiting.clear();
+
+  for (auto& p : remote_scrubs)
+    remove_from_waiting(p.first, false);
+  remote_scrubs.clear();
+
   clear_stack = false;
 }
 
@@ -696,3 +783,163 @@ void ScrubStack::clog_scrub_summary(CInode *in) {
 
   clog->info() << "scrub summary: " << scrub_summary();
 }
+
+void ScrubStack::dispatch(const cref_t<Message> &m)
+{
+  switch (m->get_type()) {
+  case MSG_MDS_SCRUB:
+    handle_scrub(ref_cast<MMDSScrub>(m));
+    break;
+
+  default:
+    derr << " scrub stack unknown message " << m->get_type() << dendl_impl;
+    ceph_abort_msg("scrub stack unknown message");
+  }
+}
+
+void ScrubStack::handle_scrub(const cref_t<MMDSScrub> &m)
+{
+
+  mds_rank_t from = mds_rank_t(m->get_source().num());
+  dout(10) << __func__ << " " << *m << " from mds." << from << dendl;
+
+  switch (m->get_op()) {
+  case MMDSScrub::OP_QUEUEDIR:
+    {
+      CInode *diri = mdcache->get_inode(m->get_ino());
+      ceph_assert(diri);
+
+      std::vector<CDir*> dfs;
+      MDSGatherBuilder gather(g_ceph_context);
+      for (const auto& fg : m->get_frags()) {
+       CDir *dir = diri->get_dirfrag(fg);
+       if (!dir) {
+         dout(10) << __func__ << " no frag " << fg << dendl;
+         continue;
+       }
+       if (!dir->is_auth()) {
+         dout(10) << __func__ << " not auth " << *dir << dendl;
+         continue;
+       }
+       if (!dir->can_auth_pin()) {
+         dout(10) << __func__ << " can't auth pin " << *dir <<  dendl;
+         dir->add_waiter(CDir::WAIT_UNFREEZE, gather.new_sub());
+         continue;
+       }
+       dfs.push_back(dir);
+      }
+
+      if (gather.has_subs()) {
+       gather.set_finisher(new C_MDS_RetryMessage(mdcache->mds, m));
+       gather.activate();
+       return;
+      }
+
+      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(), nullptr);
+       for (auto dir : dfs) {
+         queued.insert_raw(dir->get_frag());
+         _enqueue(dir, header, nullptr, true);
+       }
+       queued.simplify();
+       kick_off_scrubs();
+      }
+
+      auto r = make_message<MMDSScrub>(MMDSScrub::OP_QUEUEDIR_ACK, m->get_ino(),
+                                      std::move(queued), m->get_tag());
+      mdcache->mds->send_message_mds(r, from);
+    }
+    break;
+  case MMDSScrub::OP_QUEUEDIR_ACK:
+    {
+      CInode *diri = mdcache->get_inode(m->get_ino());
+      ceph_assert(diri);
+      auto it = remote_scrubs.find(diri);
+      if (it != remote_scrubs.end() &&
+         m->get_tag() == it->second.tag) {
+       if (it->second.gather_set.erase(from)) {
+         auto &queued = diri->scrub_queued_frags();
+         for (auto &fg : m->get_frags())
+           queued.insert_raw(fg);
+         queued.simplify();
+
+         if (it->second.gather_set.empty()) {
+           remote_scrubs.erase(it);
+           remove_from_waiting(diri);
+         }
+       }
+      }
+    }
+    break;
+  case MMDSScrub::OP_QUEUEINO:
+    {
+      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(), nullptr);
+
+      _enqueue(in, header, nullptr, true);
+      in->scrub_queued_frags() = m->get_frags();
+      kick_off_scrubs();
+
+      fragset_t queued;
+      auto r = make_message<MMDSScrub>(MMDSScrub::OP_QUEUEINO_ACK, m->get_ino(),
+                                      std::move(queued), m->get_tag());
+      mdcache->mds->send_message_mds(r, from);
+    }
+    break;
+  case MMDSScrub::OP_QUEUEINO_ACK:
+    {
+      CInode *in = mdcache->get_inode(m->get_ino());
+      ceph_assert(in);
+      auto it = remote_scrubs.find(in);
+      if (it != remote_scrubs.end() &&
+         m->get_tag() == it->second.tag &&
+         it->second.gather_set.erase(from)) {
+       ceph_assert(it->second.gather_set.empty());
+       remote_scrubs.erase(it);
+
+       remove_from_waiting(in, false);
+       dequeue(in);
+
+       if (in == in->scrub_info()->header->get_origin()) {
+         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);
+
+       kick_off_scrubs();
+      }
+    }
+    break;
+  default:
+    derr << " scrub stack unknown scrub operation " << m->get_op() << dendl_impl;
+    ceph_abort_msg("scrub stack unknown scrub operation");
+  }
+}
+
+void ScrubStack::handle_mds_failure(mds_rank_t mds)
+{
+  bool kick = false;
+  for (auto it = remote_scrubs.begin(); it != remote_scrubs.end(); ) {
+    if (it->second.gather_set.erase(mds) &&
+       it->second.gather_set.empty()) {
+      CInode *in = it->first;
+      remote_scrubs.erase(it++);
+      remove_from_waiting(in, false);
+      kick = true;
+    } else {
+      ++it;
+    }
+  }
+  if (kick)
+    kick_off_scrubs();
+}
index a94aeec6766608fc9f7fe6fe299042c86cc37c55..592b4e9ef643ea44eec99690e568e9d26b83da77 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "common/LogClient.h"
 #include "include/elist.h"
+#include "messages/MMDSScrub.h"
 
 class MDCache;
 class Finisher;
@@ -95,9 +96,33 @@ public:
 
   bool is_scrubbing() const { return !scrub_stack.empty(); }
 
+  void handle_mds_failure(mds_rank_t mds);
+
+  void dispatch(const cref_t<Message> &m);
+
   MDCache *mdcache;
 
 protected:
+
+  // reference to global cluster log client
+  LogChannelRef &clog;
+
+  /// A finisher needed so that we don't re-enter kick_off_scrubs
+  Finisher *finisher;
+
+  /// The stack of inodes we want to scrub
+  elist<MDSCacheObject*> scrub_stack;
+  elist<MDSCacheObject*> scrub_waiting;
+  /// current number of dentries we're actually scrubbing
+  int scrubs_in_progress = 0;
+  int stack_size = 0;
+
+  struct scrub_remote_t {
+    std::string tag;
+    std::set<mds_rank_t> gather_set;
+  };
+  std::map<CInode*, scrub_remote_t> remote_scrubs;
+
   class C_KickOffScrubs : public MDSInternalContext {
   public:
     C_KickOffScrubs(ScrubStack *s);
@@ -114,20 +139,6 @@ protected:
   private:
     ScrubStack *stack;
   };
-
-  // reference to global cluster log client
-  LogChannelRef &clog;
-
-  /// A finisher needed so that we don't re-enter kick_off_scrubs
-  Finisher *finisher;
-
-  /// The stack of inodes we want to scrub
-  elist<MDSCacheObject*> scrub_stack;
-  elist<MDSCacheObject*> scrub_waiting;
-  /// current number of dentries we're actually scrubbing
-  int scrubs_in_progress = 0;
-  int stack_size = 0;
-
   C_KickOffScrubs scrub_kick;
 
   friend class C_RetryScrub;
@@ -165,7 +176,12 @@ private:
   /**
    * Move the inode/dirfrag back to scrub queue.
    */
-  void remove_from_waiting(MDSCacheObject *obj);
+  void remove_from_waiting(MDSCacheObject *obj, bool kick=true);
+  /**
+   * Validate authority of the inode. If current mds is not auth of the inode,
+   * forword scrub to auth mds.
+   */
+  bool validate_inode_auth(CInode *in);
 
   /**
    * Scrub a file inode.
@@ -245,6 +261,8 @@ private:
    */
   void clog_scrub_summary(CInode *in=nullptr);
 
+  void handle_scrub(const cref_t<MMDSScrub> &m);
+
   State state = STATE_IDLE;
   bool clear_stack = false;
 
diff --git a/src/messages/MMDSScrub.h b/src/messages/MMDSScrub.h
new file mode 100644 (file)
index 0000000..f1a7e78
--- /dev/null
@@ -0,0 +1,124 @@
+// -*- 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_MMDSSCRUB_H
+#define CEPH_MMDSSCRUB_H
+
+#include "messages/MMDSOp.h"
+
+#include "include/types.h"
+#include "include/frag.h"
+
+class MMDSScrub : public MMDSOp {
+public:
+  static constexpr int OP_QUEUEDIR     = 1;
+  static constexpr int OP_QUEUEDIR_ACK = -1;
+  static constexpr int OP_QUEUEINO     = 2;
+  static constexpr int OP_QUEUEINO_ACK         = -2;
+
+  static const char *get_opname(int o) {
+    switch (o) {
+    case OP_QUEUEDIR: return "queue_dir";
+    case OP_QUEUEDIR_ACK: return "queue_dir_ack";
+    case OP_QUEUEINO: return "queue_ino";
+    case OP_QUEUEINO_ACK: return "queue_ino_ack";
+    default: ceph_abort(); return nullptr;
+    }
+  }
+
+  std::string_view get_type_name() const override { return "mds_scrub"; }
+
+  void print(std::ostream& out) const override {
+    out << "mds_scrub(" << get_opname(op) << " "
+       << ino << " " << frags << " " << tag;
+    if (is_force()) out << " force";
+    if (is_recursive()) out << " recursive";
+    if (is_repair()) out << " repair";
+    out << ")";
+  }
+  void encode_payload(uint64_t features) override {
+    using ceph::encode;
+    encode(op, payload);
+    encode(ino, payload);
+    encode(frags, payload);
+    encode(tag, payload);
+    encode(flags, payload);
+  }
+  void decode_payload() override {
+    using ceph::decode;
+    auto p = payload.cbegin();
+    decode(op, p);
+    decode(ino, p);
+    decode(frags, p);
+    decode(tag, p);
+    decode(flags, p);
+  }
+  inodeno_t get_ino() const {
+    return ino;
+  }
+  const fragset_t& get_frags() const {
+    return frags;
+  }
+  const std::string& get_tag() const {
+    return tag;
+  }
+  int get_op() const {
+    return op;
+  }
+  bool is_internal_tag() const {
+    return flags & FLAG_INTERNAL_TAG;
+  }
+  bool is_force() const {
+    return flags & FLAG_FORCE;
+  }
+  bool is_recursive() const {
+    return flags & FLAG_RECURSIVE;
+  }
+  bool is_repair() const {
+    return flags & FLAG_REPAIR;
+  }
+
+protected:
+  static constexpr int HEAD_VERSION = 1;
+  static constexpr int COMPAT_VERSION = 1;
+
+  MMDSScrub() : SafeMessage(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) {
+    if (internal_tag) flags |= FLAG_INTERNAL_TAG;
+    if (force) flags |= FLAG_FORCE;
+    if (recursive) flags |= FLAG_RECURSIVE;
+    if (repair) flags |= FLAG_REPAIR;
+  }
+
+  ~MMDSScrub() override {}
+private:
+  template<class T, typename... Args>
+    friend boost::intrusive_ptr<T> ceph::make_message(Args&&... args);
+
+  static constexpr unsigned FLAG_INTERNAL_TAG  = 1<<0;
+  static constexpr unsigned FLAG_FORCE         = 1<<1;
+  static constexpr unsigned FLAG_RECURSIVE     = 1<<2;
+  static constexpr unsigned FLAG_REPAIR                = 1<<3;
+
+  int op;
+  inodeno_t ino;
+  fragset_t frags;
+  std::string tag;
+  unsigned flags = 0;
+};
+#endif // CEPH_MMDSSCRUB_H
index 7f79696ca2dd107e7f55d622123e76f9ef919290..7b4408bf8ba61cef890b8cf1c308c93572d7b6e3 100644 (file)
 #include "messages/MMDSOpenIno.h"
 #include "messages/MMDSOpenInoReply.h"
 #include "messages/MMDSSnapUpdate.h"
+#include "messages/MMDSScrub.h"
 
 #include "messages/MDirUpdate.h"
 #include "messages/MDiscover.h"
@@ -760,6 +761,10 @@ Message *decode_message(CephContext *cct,
     m = make_message<MMDSFragmentNotifyAck>();
     break;
 
+  case MSG_MDS_SCRUB:
+    m = make_message<MMDSScrub>();
+    break;
+
   case MSG_MDS_EXPORTDIRDISCOVER:
     m = make_message<MExportDirDiscover>();
     break;
index 1c9cb94b1b50fd83856d394913d3fe4e453054c5..0f1e19a0c00ddfd6cfc0d0edb82a7c049989af7a 100644 (file)
 #define MSG_MDS_BEACON             100  // to monitor
 #define MSG_MDS_PEER_REQUEST       101
 #define MSG_MDS_TABLE_REQUEST      102
+#define MSG_MDS_SCRUB              135
 
                                 // 150 already in use (MSG_OSD_RECOVERY_RESERVE)