]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
osd/scrub: expose scrubbing schedule to operator
authorRonen Friedman <rfriedma@redhat.com>
Tue, 21 Sep 2021 10:53:20 +0000 (10:53 +0000)
committerRonen Friedman <rfriedma@redhat.com>
Fri, 5 Nov 2021 15:07:57 +0000 (17:07 +0200)
Add a 'scrub scheduling info' column to pgs dump.
Modify the name and behavior of 'last-scrub-duration'.

Signed-off-by: Ronen Friedman <rfriedma@redhat.com>
Co-Authored-By: Aishwarya Mathuria <amathuri@redhat.com>
13 files changed:
src/include/utime_fmt.h [new file with mode: 0644]
src/mon/PGMap.cc
src/osd/PG.cc
src/osd/PeeringState.cc
src/osd/PeeringState.h
src/osd/PrimaryLogPG.cc
src/osd/osd_types.cc
src/osd/osd_types.h
src/osd/scrubber/osd_scrub_sched.cc
src/osd/scrubber/osd_scrub_sched.h
src/osd/scrubber/pg_scrubber.cc
src/osd/scrubber/pg_scrubber.h
src/osd/scrubber_common.h

diff --git a/src/include/utime_fmt.h b/src/include/utime_fmt.h
new file mode 100644 (file)
index 0000000..44c9a40
--- /dev/null
@@ -0,0 +1,32 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#pragma once
+/**
+ * \file fmtlib formatter for utime_t
+ */
+#include <fmt/format.h>
+#include <fmt/chrono.h>
+
+#include <string_view>
+
+#include "include/utime.h"
+
+template <>
+struct fmt::formatter<utime_t> {
+  constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
+
+  template <typename FormatContext>
+  auto format(const utime_t& utime, FormatContext& ctx)
+  {
+    if (utime.sec() < ((time_t)(60 * 60 * 24 * 365 * 10))) {
+      // raw seconds.  this looks like a relative time.
+      return fmt::format_to(ctx.out(), "{}.{:06}", (long)utime.sec(),
+                            utime.usec());
+    }
+
+    // this looks like an absolute time.
+    // conform to http://en.wikipedia.org/wiki/ISO_8601
+    auto asgmt = fmt::gmtime(utime.sec());
+    return fmt::format_to(ctx.out(), "{:%FT%T}.{:06}{:%z}", asgmt, utime.usec(), asgmt);
+  }
+};
index cb90535addd22de9fc1b33cdea769cf888ebbc41..d773c2fd3861fecbbdb13ca9b95345dd0c487ea6 100644 (file)
@@ -1657,7 +1657,8 @@ void PGMap::dump_pg_stats_plain(
     tab.define_column("LAST_DEEP_SCRUB", TextTable::LEFT, TextTable::RIGHT);
     tab.define_column("DEEP_SCRUB_STAMP", TextTable::LEFT, TextTable::RIGHT);
     tab.define_column("SNAPTRIMQ_LEN", TextTable::LEFT, TextTable::RIGHT);
-    tab.define_column("SCRUB_DURATION", TextTable::LEFT, TextTable::RIGHT);
+    tab.define_column("LAST_SCRUB_DURATION", TextTable::LEFT, TextTable::RIGHT);
+    tab.define_column("SCRUB_SCHEDULING", TextTable::LEFT, TextTable::LEFT);
   }
 
   for (auto i = pg_stats.begin();
@@ -2230,7 +2231,8 @@ void PGMap::dump_filtered_pg_stats(ostream& ss, set<pg_t>& pgs) const
   tab.define_column("ACTING", TextTable::LEFT, TextTable::RIGHT);
   tab.define_column("SCRUB_STAMP", TextTable::LEFT, TextTable::RIGHT);
   tab.define_column("DEEP_SCRUB_STAMP", TextTable::LEFT, TextTable::RIGHT);
-  tab.define_column("SCRUB_DURATION", TextTable::LEFT, TextTable::RIGHT);
+  tab.define_column("LAST_SCRUB_DURATION", TextTable::LEFT, TextTable::RIGHT);
+  tab.define_column("SCRUB_SCHEDULING", TextTable::LEFT, TextTable::LEFT);
 
   for (auto i = pgs.begin(); i != pgs.end(); ++i) {
     const pg_stat_t& st = pg_stat.at(*i);
@@ -2258,8 +2260,9 @@ void PGMap::dump_filtered_pg_stats(ostream& ss, set<pg_t>& pgs) const
         << actingstr.str()
         << st.last_scrub_stamp
         << st.last_deep_scrub_stamp
-        << st.scrub_duration
-        << TextTable::endrow;
+        << st.last_scrub_duration
+        << st.dump_scrub_schedule()
+      << TextTable::endrow;
   }
 
   ss << tab;
index 28e4ddb6b3071975f29212d363f3ef4cb9481deb..a68d75ed160d08ae149888b5469f84d5d28cd98b 100644 (file)
@@ -818,10 +818,17 @@ void PG::publish_stats_to_osd()
   if (!is_primary())
     return;
 
+  if (m_scrubber) {
+    recovery_state.update_stats_wo_resched(
+      [scrubber = m_scrubber.get()](pg_history_t& hist,
+                                    pg_stat_t& info) mutable -> void {
+        info.scrub_sched_status = scrubber->get_schedule();
+      });
+  }
+
   std::lock_guard l{pg_stats_publish_lock};
-  auto stats = recovery_state.prepare_stats_for_publish(
-    pg_stats_publish,
-    unstable_stats);
+  auto stats =
+    recovery_state.prepare_stats_for_publish(pg_stats_publish, unstable_stats);
   if (stats) {
     pg_stats_publish = std::move(stats);
   }
@@ -2533,12 +2540,6 @@ void PG::handle_query_state(Formatter *f)
   dout(10) << "handle_query_state" << dendl;
   PeeringState::QueryState q(f);
   recovery_state.handle_event(q, 0);
-
-  // This code has moved to after the close of recovery_state array.
-  // I don't think that scrub is a recovery state
-  if (is_primary() && is_active() && m_scrubber && m_scrubber->is_scrub_active()) {
-    m_scrubber->handle_query_state(f);
-  }
 }
 
 void PG::init_collection_pool_opts()
index 5a057b279e4e1f43e9c7149a43ba1be890529e5f..ae9344ec4ab49f2b13e99f70e2e50bb68d5d9634 100644 (file)
@@ -3984,6 +3984,13 @@ void PeeringState::update_stats(
   }
 }
 
+void PeeringState::update_stats_wo_resched(
+  std::function<void(pg_history_t &, pg_stat_t &)> f)
+{
+  f(info.history, info.stats);
+}
+
+
 bool PeeringState::append_log_entries_update_missing(
   const mempool::osd_pglog::list<pg_log_entry_t> &entries,
   ObjectStore::Transaction &t, std::optional<eversion_t> trim_to,
index 2225fcd3f4ab02e9d6625b5420d129c44f588a1a..5e676613ca55ef7afdd5e293349d2781a9244074 100644 (file)
@@ -1809,6 +1809,9 @@ public:
     std::function<bool(pg_history_t &, pg_stat_t &)> f,
     ObjectStore::Transaction *t = nullptr);
 
+  void update_stats_wo_resched(
+    std::function<void(pg_history_t &, pg_stat_t &)> f);
+
   /**
    * adjust_purged_snaps
    *
index 371ff4c088152a627898624357a3c7c9747870a1..6c2343ba34f4fe17099cc3cae1ca0fa66c6d0dd9 100644 (file)
@@ -1032,7 +1032,7 @@ void PrimaryLogPG::do_command(
     f->close_section();
 
     if (is_primary() && is_active() && m_scrubber) {
-      m_scrubber->dump(f.get());
+      m_scrubber->dump_scrubber(f.get(), m_planned_scrub);
     }
 
     f->open_object_section("agent_state");
index 3bb4dfbab4e4f5b01ed1299be5d026aff0e1a4da..9e260de9426ea439c5044c5bb2506fb51df745b9 100644 (file)
@@ -36,8 +36,10 @@ extern "C" {
 
 #include "common/Formatter.h"
 #include "common/StackStringStream.h"
+#include "include/utime_fmt.h"
 #include "OSDMap.h"
 #include "osd_types.h"
+#include "osd_types_fmt.h"
 #include "os/Transaction.h"
 
 using std::list;
@@ -2856,7 +2858,8 @@ void pg_stat_t::dump(Formatter *f) const
   f->dump_bool("pin_stats_invalid", pin_stats_invalid);
   f->dump_bool("manifest_stats_invalid", manifest_stats_invalid);
   f->dump_unsigned("snaptrimq_len", snaptrimq_len);
-  f->dump_float("scrub_duration", scrub_duration);
+  f->dump_int("last_scrub_duration", last_scrub_duration);
+  f->dump_string("scrub_schedule", dump_scrub_schedule());
   stats.dump(f);
   f->open_array_section("up");
   for (auto p = up.cbegin(); p != up.cend(); ++p)
@@ -2909,6 +2912,47 @@ void pg_stat_t::dump_brief(Formatter *f) const
   f->dump_int("acting_primary", acting_primary);
 }
 
+std::string pg_stat_t::dump_scrub_schedule() const
+{
+  if (scrub_sched_status.m_is_active) {
+    return fmt::format(
+      "{}scrubbing for {}s",
+      ((scrub_sched_status.m_is_deep == scrub_level_t::deep) ? "deep " : ""),
+      scrub_sched_status.m_duration_seconds);
+  }
+  switch (scrub_sched_status.m_sched_status) {
+    case pg_scrub_sched_status_t::unknown:
+      // no reported scrub schedule yet
+      return "--"s;
+    case pg_scrub_sched_status_t::not_queued:
+      return "no scrub is scheduled"s;
+    case pg_scrub_sched_status_t::scheduled:
+      return fmt::format(
+        "{} {}scrub scheduled @ {}",
+        (scrub_sched_status.m_is_periodic ? "periodic" : "user requested"),
+        ((scrub_sched_status.m_is_deep == scrub_level_t::deep) ? "deep " : ""),
+        scrub_sched_status.m_scheduled_at);
+    case pg_scrub_sched_status_t::queued:
+      return fmt::format(
+        "queued for {}scrub",
+        ((scrub_sched_status.m_is_deep == scrub_level_t::deep) ? "deep " : ""));
+    default:
+      // a bug!
+      return "SCRUB STATE MISMATCH!"s;
+  }
+}
+
+bool operator==(const pg_scrubbing_status_t& l, const pg_scrubbing_status_t& r)
+{
+  return
+    l.m_sched_status == r.m_sched_status &&
+    l.m_scheduled_at == r.m_scheduled_at &&
+    l.m_duration_seconds == r.m_duration_seconds &&
+    l.m_is_active == r.m_is_active &&
+    l.m_is_deep == r.m_is_deep &&
+    l.m_is_periodic == r.m_is_periodic;
+}
+
 void pg_stat_t::encode(ceph::buffer::list &bl) const
 {
   ENCODE_START(27, 22, bl);
@@ -2959,7 +3003,13 @@ void pg_stat_t::encode(ceph::buffer::list &bl) const
   encode(manifest_stats_invalid, bl);
   encode(avail_no_missing, bl);
   encode(object_location_counts, bl);
-  encode(scrub_duration, bl);
+  encode(last_scrub_duration, bl);
+  encode(scrub_sched_status.m_scheduled_at, bl);
+  encode(scrub_sched_status.m_duration_seconds, bl);
+  encode((__u16)scrub_sched_status.m_sched_status, bl);
+  encode(scrub_sched_status.m_is_active, bl);
+  encode((scrub_sched_status.m_is_deep==scrub_level_t::deep), bl);
+  encode(scrub_sched_status.m_is_periodic, bl);
   ENCODE_FINISH(bl);
 }
 
@@ -3035,7 +3085,18 @@ void pg_stat_t::decode(ceph::buffer::list::const_iterator &bl)
       decode(object_location_counts, bl);
     }
     if (struct_v >= 27) {
-      decode(scrub_duration, bl);
+      decode(last_scrub_duration, bl);
+      decode(scrub_sched_status.m_scheduled_at, bl);
+      decode(scrub_sched_status.m_duration_seconds, bl);
+      __u16 scrub_sched_as_u16;
+      decode(scrub_sched_as_u16, bl);
+      scrub_sched_status.m_sched_status = (pg_scrub_sched_status_t)(scrub_sched_as_u16);
+      decode(tmp, bl);
+      scrub_sched_status.m_is_active = tmp;
+      decode(tmp, bl);
+      scrub_sched_status.m_is_deep = tmp ? scrub_level_t::deep : scrub_level_t::shallow;
+      decode(tmp, bl);
+      scrub_sched_status.m_is_periodic = tmp;
     }
   }
   DECODE_FINISH(bl);
@@ -3069,7 +3130,7 @@ void pg_stat_t::generate_test_instances(list<pg_stat_t*>& o)
   a.last_deep_scrub = eversion_t(13, 14);
   a.last_deep_scrub_stamp = utime_t(15, 16);
   a.last_clean_scrub_stamp = utime_t(17, 18);
-  a.scrub_duration = 0.003;
+  a.last_scrub_duration = 3617;
   a.snaptrimq_len = 1048576;
   list<object_stat_collection_t*> l;
   object_stat_collection_t::generate_test_instances(l);
@@ -3144,7 +3205,8 @@ bool operator==(const pg_stat_t& l, const pg_stat_t& r)
     l.manifest_stats_invalid == r.manifest_stats_invalid &&
     l.purged_snaps == r.purged_snaps &&
     l.snaptrimq_len == r.snaptrimq_len &&
-    l.scrub_duration == r.scrub_duration;
+    l.last_scrub_duration == r.last_scrub_duration &&
+    l.scrub_sched_status == r.scrub_sched_status;
 }
 
 // -- store_statfs_t --
index 4f65e14d55d9fbe39f63320c03c9f37d1c532a52..1bb384548d6d6d129b08686cb110b601eda2f73d 100644 (file)
@@ -2179,6 +2179,28 @@ inline bool operator==(const object_stat_collection_t& l,
   return l.sum == r.sum;
 }
 
+enum class scrub_level_t : bool { shallow = false, deep = true };
+enum class scrub_type_t : bool { not_repair = false, do_repair = true };
+
+/// is there a scrub in our future?
+enum class pg_scrub_sched_status_t : uint16_t {
+  unknown,         ///< status not reported yet
+  not_queued,     ///< not in the OSD's scrub queue. Probably not active.
+  active,          ///< scrubbing
+  scheduled,      ///< scheduled for a scrub at an already determined time
+  queued          ///< queued to be scrubbed
+};
+
+struct pg_scrubbing_status_t {
+  utime_t m_scheduled_at{};
+  int32_t m_duration_seconds{0}; // relevant when scrubbing
+  pg_scrub_sched_status_t m_sched_status{pg_scrub_sched_status_t::unknown};
+  bool m_is_active{false};
+  scrub_level_t m_is_deep{scrub_level_t::shallow};
+  bool m_is_periodic{true};
+};
+
+bool operator==(const pg_scrubbing_status_t& l, const pg_scrubbing_status_t& r);
 
 /** pg_stat
  * aggregate stats for a single PG.
@@ -2213,6 +2235,7 @@ struct pg_stat_t {
   utime_t last_scrub_stamp;
   utime_t last_deep_scrub_stamp;
   utime_t last_clean_scrub_stamp;
+  int32_t last_scrub_duration{0};
 
   object_stat_collection_t stats;
 
@@ -2239,6 +2262,8 @@ struct pg_stat_t {
   // absurd already, so cap it to 2^32 and save 4 bytes at  the same time
   uint32_t snaptrimq_len;
 
+  pg_scrubbing_status_t scrub_sched_status;
+
   bool stats_invalid:1;
   /// true if num_objects_dirty is not accurate (because it was not
   /// maintained starting from pool creation)
@@ -2249,8 +2274,6 @@ struct pg_stat_t {
   bool pin_stats_invalid:1;
   bool manifest_stats_invalid:1;
 
-  double scrub_duration;
-
   pg_stat_t()
     : reported_seq(0),
       reported_epoch(0),
@@ -2268,8 +2291,7 @@ struct pg_stat_t {
       hitset_stats_invalid(false),
       hitset_bytes_stats_invalid(false),
       pin_stats_invalid(false),
-      manifest_stats_invalid(false),
-      scrub_duration(0)
+      manifest_stats_invalid(false)
   { }
 
   epoch_t get_effective_last_epoch_clean() const {
@@ -2329,6 +2351,7 @@ struct pg_stat_t {
   bool is_acting_osd(int32_t osd, bool primary) const;
   void dump(ceph::Formatter *f) const;
   void dump_brief(ceph::Formatter *f) const;
+  std::string dump_scrub_schedule() const;
   void encode(ceph::buffer::list &bl) const;
   void decode(ceph::buffer::list::const_iterator &bl);
   static void generate_test_instances(std::list<pg_stat_t*>& o);
@@ -6082,9 +6105,6 @@ struct PushOp {
 WRITE_CLASS_ENCODER_FEATURES(PushOp)
 std::ostream& operator<<(std::ostream& out, const PushOp &op);
 
-enum class scrub_level_t : bool { shallow = false, deep = true };
-enum class scrub_type_t : bool { not_repair = false, do_repair = true };
-
 /*
  * summarize pg contents for purposes of a scrub
  */
index 6cba89094c691124794ccfbd681b5961bf8d25dd..99f73b13164886e60e7daa3e909d9985ecf3d915 100644 (file)
@@ -2,8 +2,9 @@
 // vim: ts=8 sw=2 smarttab
 #include "./osd_scrub_sched.h"
 
-#include "include/utime.h"
+#include "include/utime_fmt.h"
 #include "osd/OSD.h"
+#include "osd/osd_types_fmt.h"
 
 #include "pg_scrubber.h"
 
@@ -48,6 +49,26 @@ void ScrubQueue::ScrubJob::update_schedule(
           << registration_state() << dendl;
 }
 
+std::string ScrubQueue::ScrubJob::scheduling_state(utime_t now_is,
+                                                  bool is_deep_expected) const
+{
+  // if not in the OSD scheduling queues, not a candidate for scrubbing
+  if (state != qu_state_t::registered) {
+    return "no scrub is scheduled";
+  }
+
+  // if the time has passed, we are surely in the queue
+  // (note that for now we do not tell client if 'penalized')
+  if (now_is > schedule.scheduled_at) {
+    // we are never sure that the next scrub will indeed be shallow:
+    return fmt::format("queued for {}scrub", (is_deep_expected ? "deep " : ""));
+  }
+
+  return fmt::format("{}scrub scheduled @ {}", (is_deep_expected ? "deep " : ""),
+                    schedule.scheduled_at);
+}
+
+
 // ////////////////////////////////////////////////////////////////////////// //
 // ScrubQueue
 
index f68942568f89719798aad83a7456d7fc571fc789..fc34be7a2019b77c7ea423dadf4b381255693270 100644 (file)
@@ -139,6 +139,12 @@ class ScrubQueue {
                                                       : " not-queued";
     }
 
+    /**
+     * a text description of the "scheduling intentions" of this PG:
+     * are we already scheduled for a scrub/deep scrub? when?
+     */
+    std::string scheduling_state(utime_t now_is, bool is_deep_expected) const;
+
     friend std::ostream& operator<<(std::ostream& out, const ScrubJob& pg);
   };
 
index df7fe95a41db819b6ee50887c471b5a190e2c7db..9e3256163e90186d300c91f1e1cdf1f2596c0a6e 100644 (file)
@@ -578,6 +578,7 @@ void PgScrubber::scrub_requested(scrub_level_t scrub_level,
   dout(20) << __func__ << " pg(" << m_pg_id << ") planned:" << req_flags << dendl;
 
   update_scrub_job(req_flags);
+  m_pg->publish_stats_to_osd();
 }
 
 
@@ -978,6 +979,7 @@ void PgScrubber::on_init()
 
   m_start = m_pg->info.pgid.pgid.get_hobj_start();
   m_active = true;
+  m_pg->publish_stats_to_osd();
 }
 
 void PgScrubber::on_replica_init()
@@ -1923,42 +1925,127 @@ void PgScrubber::on_digest_updates()
  * is cleared once scrubbing starts; Some of the values dumped here are
  * thus transitory.
  */
-void PgScrubber::dump(ceph::Formatter* f) const
+void PgScrubber::dump_scrubber(ceph::Formatter* f,
+                              const requested_scrub_t& request_flags) const
 {
   f->open_object_section("scrubber");
+
+  if (m_active) {  // TBD replace with PR#42780's test
+    f->dump_bool("active", true);
+    dump_active_scrubber(f, state_test(PG_STATE_DEEP_SCRUB));
+  } else {
+    f->dump_bool("active", false);
+    f->dump_bool("must_scrub",
+                (m_pg->m_planned_scrub.must_scrub || m_flags.required));
+    f->dump_bool("must_deep_scrub", request_flags.must_deep_scrub);
+    f->dump_bool("must_repair", request_flags.must_repair);
+    f->dump_bool("need_auto", request_flags.need_auto);
+
+    f->dump_stream("scrub_reg_stamp") << m_scrub_job->get_sched_time();
+
+    // note that we are repeating logic that is coded elsewhere (currently PG.cc).
+    // This is not optimal.
+    bool deep_expected = (ceph_clock_now() >= m_pg->next_deepscrub_interval()) ||
+                        request_flags.must_deep_scrub || request_flags.need_auto;
+    auto sched_state =
+      m_scrub_job->scheduling_state(ceph_clock_now(), deep_expected);
+    f->dump_string("schedule", sched_state);
+  }
+
+  f->close_section();
+}
+
+void PgScrubber::dump_active_scrubber(ceph::Formatter* f, bool is_deep) const
+{
   f->dump_stream("epoch_start") << m_interval_start;
-  f->dump_bool("active", m_active);
-  if (m_active) {
-    f->dump_stream("start") << m_start;
-    f->dump_stream("end") << m_end;
-    f->dump_stream("m_max_end") << m_max_end;
-    f->dump_stream("subset_last_update") << m_subset_last_update;
-    f->dump_bool("deep", m_is_deep);
-    f->dump_bool("must_scrub", (m_pg->m_planned_scrub.must_scrub || m_flags.required));
-    f->dump_bool("must_deep_scrub", m_pg->m_planned_scrub.must_deep_scrub);
-    f->dump_bool("must_repair", m_pg->m_planned_scrub.must_repair);
-    f->dump_bool("need_auto", m_pg->m_planned_scrub.need_auto);
-    f->dump_bool("req_scrub", m_flags.required);
-    f->dump_bool("time_for_deep", m_pg->m_planned_scrub.time_for_deep);
-    f->dump_bool("auto_repair", m_flags.auto_repair);
-    f->dump_bool("check_repair", m_flags.check_repair);
-    f->dump_bool("deep_scrub_on_error", m_flags.deep_scrub_on_error);
-    f->dump_stream("scrub_reg_stamp") << m_scrub_job->get_sched_time();  // utime_t
-    f->dump_unsigned("priority", m_flags.priority);
-    f->dump_int("shallow_errors", m_shallow_errors);
-    f->dump_int("deep_errors", m_deep_errors);
-    f->dump_int("fixed", m_fixed_count);
-    {
-      f->open_array_section("waiting_on_whom");
-      for (const auto& p : m_maps_status.get_awaited()) {
-       f->dump_stream("shard") << p;
-      }
-      f->close_section();
+  f->dump_stream("start") << m_start;
+  f->dump_stream("end") << m_end;
+  f->dump_stream("max_end") << m_max_end;
+  f->dump_stream("subset_last_update") << m_subset_last_update;
+  // note that m_is_deep will be set some time after PG_STATE_DEEP_SCRUB is
+  // asserted. Thus, using the latter.
+  f->dump_bool("deep", is_deep);
+
+  // dump the scrub-type flags
+  f->dump_bool("req_scrub", m_flags.required);
+  f->dump_bool("auto_repair", m_flags.auto_repair);
+  f->dump_bool("check_repair", m_flags.check_repair);
+  f->dump_bool("deep_scrub_on_error", m_flags.deep_scrub_on_error);
+  f->dump_unsigned("priority", m_flags.priority);
+
+  f->dump_int("shallow_errors", m_shallow_errors);
+  f->dump_int("deep_errors", m_deep_errors);
+  f->dump_int("fixed", m_fixed_count);
+  {
+    f->open_array_section("waiting_on_whom");
+    for (const auto& p : m_maps_status.get_awaited()) {
+      f->dump_stream("shard") << p;
     }
+    f->close_section();
   }
-  f->close_section();
+  f->dump_string("schedule", "scrubbing");
 }
 
+pg_scrubbing_status_t PgScrubber::get_schedule() const
+{
+  dout(25) << __func__ << dendl;
+
+  if (!m_scrub_job) {
+    return pg_scrubbing_status_t{};
+  }
+
+  auto now_is = ceph_clock_now();
+
+  if (m_active) {
+    // report current scrub info, including updated duration
+    int32_t duration = (utime_t{now_is} - scrub_begin_stamp).sec();
+
+    return pg_scrubbing_status_t{
+      utime_t{},
+      duration,
+      pg_scrub_sched_status_t::active,
+      true,  // active
+      (m_is_deep ? scrub_level_t::deep : scrub_level_t::shallow),
+      false /* is periodic? unknown, actually */};
+  }
+  if (m_scrub_job->state != ScrubQueue::qu_state_t::registered) {
+    return pg_scrubbing_status_t{utime_t{},
+                                 0,
+                                 pg_scrub_sched_status_t::not_queued,
+                                 false,
+                                 scrub_level_t::shallow,
+                                 false};
+  }
+
+  // Will next scrub surely be a deep one? note that deep-scrub might be
+  // selected even if we report a regular scrub here.
+  bool deep_expected = (now_is >= m_pg->next_deepscrub_interval()) ||
+                       m_pg->m_planned_scrub.must_deep_scrub ||
+                       m_pg->m_planned_scrub.need_auto;
+  scrub_level_t expected_level =
+    deep_expected ? scrub_level_t::deep : scrub_level_t::shallow;
+  bool periodic = !m_pg->m_planned_scrub.must_scrub &&
+                  !m_pg->m_planned_scrub.need_auto &&
+                  !m_pg->m_planned_scrub.must_deep_scrub;
+
+  // are we ripe for scrubbing?
+  if (now_is > m_scrub_job->schedule.scheduled_at) {
+    // we are waiting for our turn at the OSD.
+    return pg_scrubbing_status_t{m_scrub_job->schedule.scheduled_at,
+                                 0,
+                                 pg_scrub_sched_status_t::queued,
+                                 false,
+                                 expected_level,
+                                 periodic};
+  }
+
+  return pg_scrubbing_status_t{m_scrub_job->schedule.scheduled_at,
+                               0,
+                               pg_scrub_sched_status_t::scheduled,
+                               false,
+                               expected_level,
+                               periodic};
+}
 
 void PgScrubber::handle_query_state(ceph::Formatter* f)
 {
@@ -1969,8 +2056,8 @@ void PgScrubber::handle_query_state(ceph::Formatter* f)
   f->dump_bool("scrubber.active", m_active);
   f->dump_stream("scrubber.start") << m_start;
   f->dump_stream("scrubber.end") << m_end;
-  f->dump_stream("scrubber.m_max_end") << m_max_end;
-  f->dump_stream("scrubber.m_subset_last_update") << m_subset_last_update;
+  f->dump_stream("scrubber.max_end") << m_max_end;
+  f->dump_stream("scrubber.subset_last_update") << m_subset_last_update;
   f->dump_bool("scrubber.deep", m_is_deep);
   {
     f->open_array_section("scrubber.waiting_on_whom");
@@ -2012,14 +2099,14 @@ void PgScrubber::set_scrub_begin_time() {
   scrub_begin_stamp = ceph_clock_now();
 }
 
-void PgScrubber::set_scrub_duration() {
-   utime_t stamp = ceph_clock_now();
-   utime_t duration = stamp - scrub_begin_stamp;
-   m_pg->recovery_state.update_stats(
-      [=](auto &history, auto &stats) {
-       stats.scrub_duration = double(duration);
-  return true;
-    });
+void PgScrubber::set_scrub_duration()
+{
+  utime_t stamp = ceph_clock_now();
+  utime_t duration = stamp - scrub_begin_stamp;
+  m_pg->recovery_state.update_stats([=](auto& history, auto& stats) {
+    stats.last_scrub_duration = ceill(duration.to_msec()/1000.0);
+    return true;
+  });
 }
 
 void PgScrubber::reserve_replicas()
@@ -2091,8 +2178,6 @@ void PgScrubber::replica_handling_done()
   state_clear(PG_STATE_DEEP_SCRUB);
 
   reset_internal_state();
-
-  m_pg->publish_stats_to_osd();
 }
 
 /*
index a9845efb1dd5c24027466bba6352f5cc23ddb46f..895ff1232daa8ef99c5d59b80407dd420b9bc1ad 100644 (file)
@@ -286,7 +286,10 @@ class PgScrubber : public ScrubPgIF, public ScrubMachineListener {
 
   void handle_query_state(ceph::Formatter* f) final;
 
-  void dump(ceph::Formatter* f) const override;
+  pg_scrubbing_status_t get_schedule() const final;
+
+  void dump_scrubber(ceph::Formatter* f,
+                    const requested_scrub_t& request_flags) const final;
 
   // used if we are a replica
 
@@ -481,6 +484,9 @@ class PgScrubber : public ScrubPgIF, public ScrubMachineListener {
 
   void run_callbacks();
 
+  // 'query' command data for an active scrub
+  void dump_active_scrubber(ceph::Formatter* f, bool is_deep) const;
+
   // -----     methods used to verify the relevance of incoming events:
 
   /**
index 2f4376a8b7bfda82c78d461caa23b96c18d857cb..12302dad2abb2173c23a8e5676a56d5132bdb116 100644 (file)
@@ -192,7 +192,10 @@ struct ScrubPgIF {
 
   virtual void handle_query_state(ceph::Formatter* f) = 0;
 
-  virtual void dump(ceph::Formatter* f) const = 0;
+  virtual pg_scrubbing_status_t get_schedule() const = 0;
+
+  virtual void dump_scrubber(ceph::Formatter* f,
+                            const requested_scrub_t& request_flags) const = 0;
 
   /**
    * Return true if soid is currently being scrubbed and pending IOs should block.