]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mclock : Refactor the mClock scheduler source code
authorMohit Agrawal <moagrawa@redhat.com>
Tue, 27 May 2025 06:23:06 +0000 (11:53 +0530)
committerMohit Agrawal <moagrawa@redhat.com>
Mon, 9 Jun 2025 11:50:01 +0000 (17:20 +0530)
Refactor the mClock scheduler source code into a common module
so that both Classic and Crimson OSDs can use it.

Fixes: https://tracker.ceph.com/issues/71445
Signed-off-by: Mohit Agrawal <moagrawa@redhat.com>
src/common/CMakeLists.txt
src/common/mclock_common.cc [new file with mode: 0644]
src/common/mclock_common.h [new file with mode: 0644]
src/osd/scheduler/OpSchedulerItem.cc
src/osd/scheduler/OpSchedulerItem.h
src/osd/scheduler/mClockScheduler.cc
src/osd/scheduler/mClockScheduler.h
src/test/osd/TestMClockScheduler.cc

index ae34d330813f36b92f71831bd6a39f4f0463d7f3..53e225b47ba09f566afcf93dd5aa9069a7c8e719 100644 (file)
@@ -105,8 +105,10 @@ set(common_srcs
   utf8.c
   util.cc
   version.cc
+  mclock_common.cc
   tcp_info.cc)
 
+include_directories(${CMAKE_SOURCE_DIR}/src/dmclock/support/src)
 if(WITH_SYSTEMD)
   list(APPEND common_srcs
     Journald.cc)
diff --git a/src/common/mclock_common.cc b/src/common/mclock_common.cc
new file mode 100644 (file)
index 0000000..f560813
--- /dev/null
@@ -0,0 +1,517 @@
+// -*- 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) 2025 IBM, Red Hat
+ *
+ * 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.
+ *
+ */
+
+#include <memory>
+#include <functional>
+
+#include "mclock_common.h"
+#include "debug.h"
+
+#ifdef WITH_CRIMSON
+#include "crimson/common/perf_counters_collection.h"
+#else
+#include "perf_counters_collection.h"
+#endif
+
+#define dout_context cct
+#define dout_subsys ceph_subsys_mclock
+#undef dout_prefix
+#define dout_prefix *_dout << "mclock_common: "
+
+namespace dmc = crimson::dmclock;
+
+std::ostream &operator<<(std::ostream &lhs, const SchedulerClass &c)
+{
+  lhs << static_cast<size_t>(c);
+  switch (c) {
+  case SchedulerClass::background_best_effort:
+    return lhs << "background_best_effort";
+  case SchedulerClass::background_recovery:
+    return lhs << "background_recovery";
+  case SchedulerClass::client:
+    return lhs << "client";
+#ifdef WITH_CRIMSON
+  case SchedulerClass::repop:
+    return lhs << "repop";
+#endif
+  case SchedulerClass::immediate:
+    return lhs << "immediate";
+  default:
+    return lhs;
+  }
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         const client_profile_id_t& client_profile) {
+    out << " client_id: " << client_profile.client_id
+        << " profile_id: " << client_profile.profile_id;
+    return out;
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         const scheduler_id_t& sched_id) {
+    out << "{ class_id: " << sched_id.class_id
+        << sched_id.client_profile_id;
+    return out << " }";
+}
+
+/* ClientRegistry holds the dmclock::ClientInfo configuration parameters
+ * (reservation (bytes/second), weight (unitless), limit (bytes/second))
+ * for each IO class in the OSD (client, background_recovery,
+ * background_best_effort).
+ *
+ * mclock expects limit and reservation to have units of <cost>/second
+ * (bytes/second), but osd_mclock_scheduler_client_(lim|res) are provided
+ * as ratios of the OSD's capacity.  We convert from the one to the other
+ * using the capacity_per_shard parameter.
+ *
+ * Note, mclock profile information will already have been set as a default
+ * for the osd_mclock_scheduler_client_* parameters prior to calling
+ * update_from_config -- see set_config_defaults_from_profile().
+ */
+void ClientRegistry::update_from_config(const ConfigProxy &conf,
+                                        const double capacity_per_shard)
+{
+  auto get_res = [&](double res) {
+    if (res) {
+      return res * capacity_per_shard;
+    } else {
+      return default_min; // min reservation
+    }
+  };
+
+  auto get_lim = [&](double lim) {
+    if (lim) {
+      return lim * capacity_per_shard;
+    } else {
+      return default_max; // high limit
+    }
+  };
+
+  // Set external client infos
+  double res = conf.get_val<double>(
+    "osd_mclock_scheduler_client_res");
+  double lim = conf.get_val<double>(
+    "osd_mclock_scheduler_client_lim");
+  uint64_t wgt = conf.get_val<uint64_t>(
+    "osd_mclock_scheduler_client_wgt");
+  default_external_client_info.update(
+    get_res(res),
+    wgt,
+    get_lim(lim));
+
+  // Set background recovery client infos
+  res = conf.get_val<double>(
+    "osd_mclock_scheduler_background_recovery_res");
+  lim = conf.get_val<double>(
+    "osd_mclock_scheduler_background_recovery_lim");
+  wgt = conf.get_val<uint64_t>(
+    "osd_mclock_scheduler_background_recovery_wgt");
+  internal_client_infos[
+    static_cast<size_t>(SchedulerClass::background_recovery)].update(
+      get_res(res),
+      wgt,
+      get_lim(lim));
+
+  // Set background best effort client infos
+  res = conf.get_val<double>(
+    "osd_mclock_scheduler_background_best_effort_res");
+  lim = conf.get_val<double>(
+    "osd_mclock_scheduler_background_best_effort_lim");
+  wgt = conf.get_val<uint64_t>(
+    "osd_mclock_scheduler_background_best_effort_wgt");
+  internal_client_infos[
+    static_cast<size_t>(SchedulerClass::background_best_effort)].update(
+      get_res(res),
+      wgt,
+      get_lim(lim));
+}
+
+const dmc::ClientInfo *ClientRegistry::get_external_client(
+  const client_profile_id_t &client) const
+{
+  auto ret = external_client_infos.find(client);
+  if (ret == external_client_infos.end())
+    return &default_external_client_info;
+  else
+    return &(ret->second);
+}
+
+const dmc::ClientInfo *ClientRegistry::get_info(
+  const scheduler_id_t &id) const {
+  switch (id.class_id) {
+  case SchedulerClass::immediate:
+    ceph_assert(0 == "Cannot schedule immediate");
+    return (dmc::ClientInfo*)nullptr;
+  case SchedulerClass::client:
+    return get_external_client(id.client_profile_id);
+  default:
+    ceph_assert(static_cast<size_t>(id.class_id) < internal_client_infos.size());
+    return &internal_client_infos[static_cast<size_t>(id.class_id)];
+  }
+}
+
+static std::ostream &operator<<(
+  std::ostream &lhs, const profile_t::client_config_t &rhs)
+{
+  return lhs << "{res: " << rhs.reservation
+             << ", wgt: " << rhs.weight
+             << ", lim: " << rhs.limit
+             << "}";
+}
+
+
+static std::ostream &operator<<(std::ostream &lhs, const profile_t &rhs)
+{
+  return lhs << "[client: " << rhs.client
+             << ", background_recovery: " << rhs.background_recovery
+             << ", background_best_effort: " << rhs.background_best_effort
+             << "]";
+}
+
+void MclockConfig::set_config_defaults_from_profile()
+{
+  // Let only a single osd shard (id:0) set the profile configs
+  if (shard_id > 0) {
+    return;
+  }
+
+  /**
+   * high_client_ops
+   *
+   * Client Allocation:
+   *   reservation: 60% | weight: 2 | limit: 0 (max) |
+   * Background Recovery Allocation:
+   *   reservation: 40% | weight: 1 | limit: 0 (max) |
+   * Background Best Effort Allocation:
+   *   reservation: 0 (min) | weight: 1 | limit: 70% |
+   */
+  static constexpr profile_t high_client_ops_profile{
+    { .6, 2,  0 },
+    { .4, 1,  0 },
+    {  0, 1, .7 }
+  };
+
+  /**
+   * high_recovery_ops
+   *
+   * Client Allocation:
+   *   reservation: 30% | weight: 1 | limit: 0 (max) |
+   * Background Recovery Allocation:
+   *   reservation: 70% | weight: 2 | limit: 0 (max) |
+   * Background Best Effort Allocation:
+   *   reservation: 0 (min) | weight: 1 | limit: 0 (max) |
+   */
+  static constexpr profile_t high_recovery_ops_profile{
+    { .3, 1, 0 },
+    { .7, 2, 0 },
+    {  0, 1, 0 }
+  };
+
+  /**
+   * balanced
+   *
+   * Client Allocation:
+   *   reservation: 50% | weight: 1 | limit: 0 (max) |
+   * Background Recovery Allocation:
+   *   reservation: 50% | weight: 1 | limit: 0 (max) |
+   * Background Best Effort Allocation:
+   *   reservation: 0 (min) | weight: 1 | limit: 90% |
+   */
+  static constexpr profile_t balanced_profile{
+    { .5, 1, 0 },
+    { .5, 1, 0 },
+    {  0, 1, .9 }
+  };
+
+  const profile_t *profile = nullptr;
+  auto mclock_profile = cct->_conf.get_val<std::string>("osd_mclock_profile");
+  if (mclock_profile == "high_client_ops") {
+    profile = &high_client_ops_profile;
+    dout(10) << "Setting high_client_ops profile " << *profile << dendl;
+  } else if (mclock_profile == "high_recovery_ops") {
+    profile = &high_recovery_ops_profile;
+    dout(10) << "Setting high_recovery_ops profile " << *profile << dendl;
+  } else if (mclock_profile == "balanced") {
+    profile = &balanced_profile;
+    dout(10) << "Setting balanced profile " << *profile << dendl;
+  } else if (mclock_profile == "custom") {
+    dout(10) << "Profile set to custom, not setting defaults" << dendl;
+    return;
+  } else {
+    derr << "Invalid mclock profile: " << mclock_profile << dendl;
+    ceph_assert("Invalid choice of mclock profile" == 0);
+    return;
+  }
+  ceph_assert(nullptr != profile);
+
+  auto set_config = [&conf = cct->_conf](const char *key, auto val) {
+    conf.set_val_default(key, std::to_string(val));
+  };
+
+  set_config("osd_mclock_scheduler_client_res", profile->client.reservation);
+  set_config("osd_mclock_scheduler_client_wgt", profile->client.weight);
+  set_config("osd_mclock_scheduler_client_lim", profile->client.limit);
+
+  set_config(
+    "osd_mclock_scheduler_background_recovery_res",
+    profile->background_recovery.reservation);
+  set_config(
+    "osd_mclock_scheduler_background_recovery_wgt",
+    profile->background_recovery.weight);
+  set_config(
+    "osd_mclock_scheduler_background_recovery_lim",
+    profile->background_recovery.limit);
+
+  set_config(
+    "osd_mclock_scheduler_background_best_effort_res",
+    profile->background_best_effort.reservation);
+  set_config(
+    "osd_mclock_scheduler_background_best_effort_wgt",
+    profile->background_best_effort.weight);
+  set_config(
+    "osd_mclock_scheduler_background_best_effort_lim",
+    profile->background_best_effort.limit);
+
+  cct->_conf.apply_changes(nullptr);
+}
+
+void MclockConfig::set_osd_capacity_params_from_config()
+{
+  uint64_t osd_bandwidth_capacity;
+  double osd_iop_capacity;
+
+  std::tie(osd_bandwidth_capacity, osd_iop_capacity) = [&] {
+    if (is_rotational) {
+      return std::make_tuple(
+        cct->_conf.get_val<Option::size_t>(
+          "osd_mclock_max_sequential_bandwidth_hdd"),
+        cct->_conf.get_val<double>("osd_mclock_max_capacity_iops_hdd"));
+    } else {
+      return std::make_tuple(
+        cct->_conf.get_val<Option::size_t>(
+          "osd_mclock_max_sequential_bandwidth_ssd"),
+        cct->_conf.get_val<double>("osd_mclock_max_capacity_iops_ssd"));
+    }
+  }();
+
+  osd_bandwidth_capacity = std::max<uint64_t>(1, osd_bandwidth_capacity);
+  osd_iop_capacity = std::max<double>(1.0, osd_iop_capacity);
+
+  osd_bandwidth_cost_per_io =
+    static_cast<double>(osd_bandwidth_capacity) / osd_iop_capacity;
+  osd_bandwidth_capacity_per_shard =
+    static_cast<double>(osd_bandwidth_capacity) /
+    static_cast<double>(num_shards);
+  dout(1) << __func__ << ": osd_bandwidth_cost_per_io: "
+          << std::fixed << std::setprecision(2)
+          << osd_bandwidth_cost_per_io << " bytes/io"
+          << ", osd_bandwidth_capacity_per_shard "
+          << osd_bandwidth_capacity_per_shard << " bytes/second"
+          << dendl;
+}
+
+void MclockConfig::init_logger()
+{
+  PerfCountersBuilder m(cct, "mclock-shard-queue-" + std::to_string(shard_id),
+                        l_mclock_first, l_mclock_last);
+
+  m.add_u64_counter(l_mclock_immediate_queue_len, "mclock_immediate_queue_len",
+                    "high_priority op count in mclock queue");
+  m.add_u64_counter(l_mclock_client_queue_len, "mclock_client_queue_len",
+                    "client type op count in mclock queue");
+  m.add_u64_counter(l_mclock_recovery_queue_len, "mclock_recovery_queue_len",
+                    "background_recovery type op count in mclock queue");
+  m.add_u64_counter(l_mclock_best_effort_queue_len, "mclock_best_effort_queue_len",
+                    "background_best_effort type op count in mclock queue");
+  m.add_u64_counter(l_mclock_all_type_queue_len, "mclock_all_type_queue_len",
+                    "all type op count in mclock queue");
+
+  logger = m.create_perf_counters();
+  cct->get_perfcounters_collection()->add(logger);
+
+  logger->set(l_mclock_immediate_queue_len, 0);
+  logger->set(l_mclock_client_queue_len, 0);
+  logger->set(l_mclock_recovery_queue_len, 0);
+  logger->set(l_mclock_best_effort_queue_len, 0);
+  logger->set(l_mclock_all_type_queue_len, 0);
+}
+
+void MclockConfig::get_mclock_counter(scheduler_id_t id)
+{
+  if (!logger) {
+    return;
+  }
+
+  /* op enter mclock queue will +1 */
+  logger->inc(l_mclock_all_type_queue_len);
+
+  switch (id.class_id) {
+  case SchedulerClass::immediate:
+    logger->inc(l_mclock_immediate_queue_len);
+    break;
+  case SchedulerClass::client:
+    logger->inc(l_mclock_client_queue_len);
+    break;
+  case SchedulerClass::background_recovery:
+    logger->inc(l_mclock_recovery_queue_len);
+    break;
+  case SchedulerClass::background_best_effort:
+    logger->inc(l_mclock_best_effort_queue_len);
+    break;
+   default:
+    derr << __func__ << " unknown class_id=" << id.class_id
+         << " unknown id=" << id << dendl;
+    break;
+  }
+}
+
+void MclockConfig::put_mclock_counter(scheduler_id_t id)
+{
+  if (!logger) {
+    return;
+  }
+
+  /* op leave mclock queue will -1 */
+  logger->dec(l_mclock_all_type_queue_len);
+
+  switch (id.class_id) {
+  case SchedulerClass::immediate:
+    logger->dec(l_mclock_immediate_queue_len);
+    break;
+  case SchedulerClass::client:
+    logger->dec(l_mclock_client_queue_len);
+    break;
+  case SchedulerClass::background_recovery:
+    logger->dec(l_mclock_recovery_queue_len);
+    break;
+  case SchedulerClass::background_best_effort:
+    logger->dec(l_mclock_best_effort_queue_len);
+    break;
+   default:
+    derr << __func__ << " unknown class_id=" << id.class_id
+         << " unknown id=" << id << dendl;
+    break;
+  }
+}
+
+double MclockConfig::get_cost_per_io() const {
+    return osd_bandwidth_cost_per_io;
+}
+
+double MclockConfig::get_capacity_per_shard() const {
+    return  osd_bandwidth_capacity_per_shard;
+}
+
+uint32_t MclockConfig::calc_scaled_cost(int item_cost)
+{
+  auto cost = static_cast<uint32_t>(
+    std::max<int>(
+      1, // ensure cost is non-zero and positive
+      item_cost));
+  auto cost_per_io = static_cast<uint32_t>(osd_bandwidth_cost_per_io);
+
+  return std::max<uint32_t>(cost, cost_per_io);
+}
+
+void MclockConfig::mclock_handle_conf_change(const ConfigProxy& conf,
+                                             const std::set<std::string>
+                                            &changed)
+{
+  if (changed.count("osd_mclock_max_capacity_iops_hdd") ||
+      changed.count("osd_mclock_max_capacity_iops_ssd")) {
+   set_osd_capacity_params_from_config();
+   client_registry.update_from_config(
+      conf, osd_bandwidth_capacity_per_shard);
+  }
+  if (changed.count("osd_mclock_max_sequential_bandwidth_hdd") ||
+      changed.count("osd_mclock_max_sequential_bandwidth_ssd")) {
+    set_osd_capacity_params_from_config();
+    client_registry.update_from_config(
+      conf, osd_bandwidth_capacity_per_shard);
+  }
+  if (changed.count("osd_mclock_profile")) {
+    set_config_defaults_from_profile();
+    client_registry.update_from_config(
+      conf, osd_bandwidth_capacity_per_shard);
+  }
+
+  auto get_changed_key = [&changed]() -> std::optional<std::string> {
+    static const std::vector<std::string> qos_params = {
+      "osd_mclock_scheduler_client_res",
+      "osd_mclock_scheduler_client_wgt",
+      "osd_mclock_scheduler_client_lim",
+      "osd_mclock_scheduler_background_recovery_res",
+      "osd_mclock_scheduler_background_recovery_wgt",
+      "osd_mclock_scheduler_background_recovery_lim",
+      "osd_mclock_scheduler_background_best_effort_res",
+      "osd_mclock_scheduler_background_best_effort_wgt",
+      "osd_mclock_scheduler_background_best_effort_lim"
+    };
+
+    for (auto &qp : qos_params) {
+      if (changed.count(qp)) {
+        return qp;
+      }
+    }
+    return std::nullopt;
+  };
+  if (auto key = get_changed_key(); key.has_value()) {
+    auto mclock_profile = cct->_conf.get_val<std::string>("osd_mclock_profile");
+    if (mclock_profile == "custom") {
+      client_registry.update_from_config(
+        conf, osd_bandwidth_capacity_per_shard);
+    } else {
+      // Attempt to change QoS parameter for a built-in profile. Restore the
+      // profile defaults by making one of the OSD shards remove the key from
+      // config monitor store. Note: monc is included in the check since the
+      // mock unit test currently doesn't initialize it.
+      if (shard_id == 0 && monc) {
+        static const std::vector<std::string> osds = {
+          "osd",
+          "osd." + std::to_string(whoami)
+        };
+
+        for (auto osd : osds) {
+          std::string cmd =
+            "{"
+              "\"prefix\": \"config rm\", "
+              "\"who\": \"" + osd + "\", "
+              "\"name\": \"" + *key + "\""
+            "}";
+          std::vector<std::string> vcmd{cmd};
+
+          dout(10) << __func__ << " Removing Key: " << *key
+                   << " for " << osd << " from Mon db" << dendl;
+                   monc->start_mon_command(vcmd, {}, nullptr, nullptr, nullptr);
+        }
+      }
+    }
+    // Alternatively, the QoS parameter, if set ephemerally for this OSD via
+    // the 'daemon' or 'tell' interfaces must be removed.
+    if (!cct->_conf.rm_val(*key)) {
+      dout(10) << __func__ << " Restored " << *key << " to default" << dendl;
+      cct->_conf.apply_changes(nullptr);
+    }
+  }
+}
+
+MclockConfig::~MclockConfig()
+{
+  if (logger) {
+    cct->get_perfcounters_collection()->remove(logger);
+    delete logger;
+    logger = nullptr;
+  }
+}
diff --git a/src/common/mclock_common.h b/src/common/mclock_common.h
new file mode 100644 (file)
index 0000000..331f61b
--- /dev/null
@@ -0,0 +1,184 @@
+// -*- 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) 2025 IBM, Red Hat
+ *
+ * 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.
+ *
+ */
+
+
+#pragma once
+#include "config.h"
+#include "ceph_context.h"
+#include "dmclock/src/dmclock_server.h"
+#ifndef WITH_CRIMSON
+ #include "mon/MonClient.h"
+#else
+ #include "crimson/mon/MonClient.h"
+#endif
+
+// scheduler class for classic
+enum class op_scheduler_class : uint8_t {
+  background_recovery = 0,
+  background_best_effort,
+  immediate,
+  client,
+};
+
+// scheduler class for Crimson
+enum class scheduler_class_t : uint8_t {
+  background_recovery = 0,
+  background_best_effort,
+  client,
+  repop,
+  immediate,
+};
+
+#ifdef WITH_CRIMSON
+using SchedulerClass = scheduler_class_t;
+#else
+using SchedulerClass = op_scheduler_class;
+#endif
+
+enum {
+  l_mclock_first = 15000,
+  l_mclock_immediate_queue_len,
+  l_mclock_client_queue_len,
+  l_mclock_recovery_queue_len,
+  l_mclock_best_effort_queue_len,
+  l_mclock_all_type_queue_len,
+  l_mclock_last,
+};
+
+constexpr double default_min = 0.0;
+constexpr double default_max = std::numeric_limits<double>::is_iec559 ?
+  std::numeric_limits<double>::infinity() :
+  std::numeric_limits<double>::max();
+
+std::ostream& operator<<(std::ostream& out, const SchedulerClass& class_id);
+
+/**
+ * profile_t
+ *
+ * mclock profile -- 3 params for each of 3 client classes
+ * 0 (min): specifies no minimum reservation
+ * 0 (max): specifies no upper limit
+ */
+
+struct profile_t {
+  struct client_config_t {
+    double reservation;
+    uint64_t weight;
+    double limit;
+  };
+  client_config_t client;
+  client_config_t background_recovery;
+  client_config_t background_best_effort;
+};
+
+struct client_profile_id_t {
+  uint64_t client_id = 0;
+  uint64_t profile_id = 0;
+
+  client_profile_id_t(uint64_t _client_id, uint64_t _profile_id) :
+    client_id(_client_id),
+    profile_id(_profile_id) {}
+
+  client_profile_id_t() = default;
+
+  auto operator<=>(const client_profile_id_t&) const = default;
+  friend std::ostream& operator<<(std::ostream& out,
+                                  const client_profile_id_t& client_profile);
+};
+
+struct scheduler_id_t {
+  SchedulerClass class_id;
+  client_profile_id_t client_profile_id;
+
+  auto operator<=>(const scheduler_id_t&) const = default;
+  friend std::ostream& operator<<(std::ostream& out,
+                                  const scheduler_id_t& sched_id);
+};
+
+
+class ClientRegistry {
+    static constexpr size_t internal_client_count =
+      static_cast<size_t>(SchedulerClass::background_best_effort) + 1;
+    std::vector<crimson::dmclock::ClientInfo> internal_client_infos;
+
+    crimson::dmclock::ClientInfo default_external_client_info = {1, 1, 1};
+    std::map<client_profile_id_t,
+             crimson::dmclock::ClientInfo> external_client_infos;
+    const crimson::dmclock::ClientInfo *get_external_client(
+      const client_profile_id_t &client) const;
+  public:
+    ClientRegistry() {
+      internal_client_infos.reserve(internal_client_count);
+      // Fill array with default ClientInfo instances
+      for (size_t i = 0; i < internal_client_count; ++i) {
+        internal_client_infos.emplace_back(1, 1, 1);
+      }
+    }
+    void update_from_config(const ConfigProxy &conf,
+      double capacity_per_shard);
+    const crimson::dmclock::ClientInfo *get_info(
+      const scheduler_id_t &id) const;
+};
+
+class MclockConfig {
+private:
+  CephContext *cct;
+  uint32_t num_shards;
+  bool is_rotational;
+  PerfCounters *logger;
+  int shard_id;
+  int whoami;
+  double osd_bandwidth_cost_per_io;
+  double osd_bandwidth_capacity_per_shard;
+  ClientRegistry& client_registry;
+  #ifndef WITH_CRIMSON
+  MonClient *monc;
+  #endif
+public:
+  #ifdef WITH_CRIMSON
+  MclockConfig(CephContext *cct, ClientRegistry& creg,
+               uint32_t num_shards, bool is_rotational, int shard_id,
+              int whoami):cct(cct),
+                           num_shards(num_shards),
+                           is_rotational(is_rotational),
+                          logger(nullptr),shard_id(shard_id),
+                           whoami(whoami), osd_bandwidth_cost_per_io(0.0),
+                          osd_bandwidth_capacity_per_shard(0.0),
+                          client_registry(creg)
+  {}
+  #else
+  MclockConfig(CephContext *cct, ClientRegistry& creg,
+               MonClient *monc, uint32_t num_shards, bool is_rotational,
+              int shard_id, int whoami):cct(cct),
+                                         num_shards(num_shards),
+                                         is_rotational(is_rotational),
+                                        logger(nullptr),shard_id(shard_id),
+                                         whoami(whoami),
+                                        osd_bandwidth_cost_per_io(0.0),
+                                        osd_bandwidth_capacity_per_shard(0.0),
+                                        client_registry(creg), monc(monc)
+  {}
+#endif
+  ~MclockConfig();
+  void set_config_defaults_from_profile();
+  void set_osd_capacity_params_from_config();
+  void init_logger();
+  void get_mclock_counter(scheduler_id_t id);
+  void put_mclock_counter(scheduler_id_t id);
+  double get_cost_per_io() const;
+  double get_capacity_per_shard() const;
+  void mclock_handle_conf_change(const ConfigProxy& conf,
+                                 const std::set<std::string> &changed);
+  uint32_t calc_scaled_cost(int item_cost);
+};
index 9d680c1b95b7b1e785ef328eb608f59d882969e0..1b11c147cbe0820bb38166456ac18aaac5e8984f 100644 (file)
 
 namespace ceph::osd::scheduler {
 
-std::ostream& operator<<(std::ostream& out, const op_scheduler_class& class_id) {
-  out << static_cast<size_t>(class_id);
-  return out;
-}
-
 void PGOpItem::run(
   OSD *osd,
   OSDShard *sdata,
index d0281cf84e7f3ca6cae6f069edddf78381137d58..fb5249c8545d0bbfafef784ce06fb42d2e80de68 100644 (file)
@@ -23,6 +23,7 @@
 #include "osd/PG.h"
 #include "osd/PGPeeringEvent.h"
 #include "messages/MOSDOp.h"
+#include "common/mclock_common.h"
 
 
 class OSD;
@@ -30,15 +31,6 @@ struct OSDShard;
 
 namespace ceph::osd::scheduler {
 
-enum class op_scheduler_class : uint8_t {
-  background_recovery = 0,
-  background_best_effort,
-  immediate,
-  client,
-};
-
-std::ostream& operator<<(std::ostream& out, const op_scheduler_class& class_id);
-
 class OpSchedulerItem {
 public:
   // Abstraction for operations queueable in the op queue
@@ -76,7 +68,7 @@ public:
     virtual std::string print() const = 0;
 
     virtual void run(OSD *osd, OSDShard *sdata, PGRef& pg, ThreadPool::TPHandle &handle) = 0;
-    virtual op_scheduler_class get_scheduler_class() const = 0;
+    virtual SchedulerClass get_scheduler_class() const = 0;
 
     virtual ~OpQueueable() {}
     friend std::ostream& operator<<(std::ostream& out, const OpQueueable& q) {
@@ -161,7 +153,7 @@ public:
     return qitem->peering_requires_pg();
   }
 
-  op_scheduler_class get_scheduler_class() const {
+  SchedulerClass get_scheduler_class() const {
     return qitem->get_scheduler_class();
   }
 
@@ -201,13 +193,13 @@ protected:
     return pgid;
   }
 
-  static op_scheduler_class priority_to_scheduler_class(int priority) {
+  static SchedulerClass priority_to_scheduler_class(int priority) {
     if (priority >= CEPH_MSG_PRIO_HIGH) {
-      return op_scheduler_class::immediate;
+      return SchedulerClass::immediate;
     } else if (priority >= PeeringState::recovery_msg_priority_t::DEGRADED) {
-      return op_scheduler_class::background_recovery;
+      return SchedulerClass::background_recovery;
     } else {
-      return op_scheduler_class::background_best_effort;
+      return SchedulerClass::background_best_effort;
     }
   }
 
@@ -240,13 +232,13 @@ public:
     return op;
   }
 
-  op_scheduler_class get_scheduler_class() const final {
+   SchedulerClass get_scheduler_class() const final {
     auto type = op->get_req()->get_type();
     if (type == CEPH_MSG_OSD_OP ||
        type == CEPH_MSG_OSD_BACKOFF) {
-      return op_scheduler_class::client;
+      return SchedulerClass::client;
     } else {
-      return op_scheduler_class::immediate;
+      return SchedulerClass::immediate;
     }
   }
 
@@ -273,8 +265,8 @@ public:
   const PGCreateInfo *creates_pg() const override {
     return evt->create_info.get();
   }
-  op_scheduler_class get_scheduler_class() const final {
-    return op_scheduler_class::immediate;
+  SchedulerClass get_scheduler_class() const final {
+    return SchedulerClass::immediate;
   }
 };
 
@@ -296,8 +288,8 @@ public:
   }
   void run(
     OSD *osd, OSDShard *sdata, PGRef& pg, ThreadPool::TPHandle &handle) final;
-  op_scheduler_class get_scheduler_class() const final {
-    return op_scheduler_class::background_best_effort;
+  SchedulerClass get_scheduler_class() const final {
+    return SchedulerClass::background_best_effort;
   }
 };
 
@@ -319,8 +311,8 @@ public:
   }
   void run(
     OSD *osd, OSDShard *sdata, PGRef& pg, ThreadPool::TPHandle &handle) final;
-  op_scheduler_class get_scheduler_class() const final {
-    return op_scheduler_class::background_best_effort;
+  SchedulerClass get_scheduler_class() const final {
+    return SchedulerClass::background_best_effort;
   }
 };
 
@@ -359,9 +351,9 @@ class PGScrubItem : public PGOpQueueable {
           OSDShard* sdata,
           PGRef& pg,
           ThreadPool::TPHandle& handle) override = 0;
-  op_scheduler_class get_scheduler_class() const final
+  SchedulerClass get_scheduler_class() const final
   {
-    return op_scheduler_class::background_best_effort;
+    return SchedulerClass::background_best_effort;
   }
 };
 
@@ -507,7 +499,7 @@ public:
   }
   void run(
     OSD *osd, OSDShard *sdata, PGRef& pg, ThreadPool::TPHandle &handle) final;
-  op_scheduler_class get_scheduler_class() const final {
+  SchedulerClass get_scheduler_class() const final {
     return priority_to_scheduler_class(priority);
   }
 };
@@ -535,7 +527,7 @@ public:
   }
   void run(
     OSD *osd, OSDShard *sdata, PGRef& pg, ThreadPool::TPHandle &handle) final;
-  op_scheduler_class get_scheduler_class() const final {
+  SchedulerClass get_scheduler_class() const final {
     return priority_to_scheduler_class(priority);
   }
 };
@@ -559,8 +551,8 @@ public:
   }
   void run(
     OSD *osd, OSDShard *sdata, PGRef& pg, ThreadPool::TPHandle &handle) final;
-  op_scheduler_class get_scheduler_class() const final {
-    return op_scheduler_class::background_best_effort;
+  SchedulerClass get_scheduler_class() const final {
+    return SchedulerClass::background_best_effort;
   }
 };
 
@@ -598,7 +590,7 @@ public:
     return op;
   }
 
-  op_scheduler_class get_scheduler_class() const final {
+  SchedulerClass get_scheduler_class() const final {
     return priority_to_scheduler_class(op->get_req()->get_priority());
   }
 
@@ -618,7 +610,7 @@ struct fmt::formatter<ceph::osd::scheduler::OpSchedulerItem> {
   {
     // matching existing op_scheduler_item_t::operator<<() format
     using class_t =
-       std::underlying_type_t<ceph::osd::scheduler::op_scheduler_class>;
+       std::underlying_type_t<SchedulerClass>;
     const auto qos_cost = opsi.was_queued_via_mclock()
                              ? fmt::format(" qos_cost {}", opsi.qos_cost)
                              : "";
index 225873a2f6436d24eb3f3c02542d2c87412889de..216e426fda1f9fb9b2f92bf5beed699d71cfec20 100644 (file)
@@ -37,374 +37,9 @@ using namespace std::literals;
 
 namespace ceph::osd::scheduler {
 
-void mClockScheduler::_get_mclock_counter(scheduler_id_t id)
-{
-  if (!logger) {
-    return;
-  }
-
-  /* op enter mclock queue will +1 */
-  logger->inc(l_mclock_all_type_queue_len);
-
-  switch (id.class_id) {
-  case op_scheduler_class::immediate:
-    logger->inc(l_mclock_immediate_queue_len);
-    break;
-  case op_scheduler_class::client:
-    logger->inc(l_mclock_client_queue_len);
-    break;
-  case op_scheduler_class::background_recovery:
-    logger->inc(l_mclock_recovery_queue_len);
-    break;
-  case op_scheduler_class::background_best_effort:
-    logger->inc(l_mclock_best_effort_queue_len);
-    break;
-   default:
-    derr << __func__ << " unknown class_id=" << id.class_id
-         << " unknown id=" << id << dendl;
-    break;
-  }
-}
-
-void mClockScheduler::_put_mclock_counter(scheduler_id_t id)
-{
-  if (!logger) {
-    return;
-  }
-
-  /* op leave mclock queue will -1 */
-  logger->dec(l_mclock_all_type_queue_len);
-
-  switch (id.class_id) {
-  case op_scheduler_class::immediate:
-    logger->dec(l_mclock_immediate_queue_len);
-    break;
-  case op_scheduler_class::client:
-    logger->dec(l_mclock_client_queue_len);
-    break;
-  case op_scheduler_class::background_recovery:
-    logger->dec(l_mclock_recovery_queue_len);
-    break;
-  case op_scheduler_class::background_best_effort:
-    logger->dec(l_mclock_best_effort_queue_len);
-    break;
-   default:
-    derr << __func__ << " unknown class_id=" << id.class_id
-         << " unknown id=" << id << dendl;
-    break;
-  }
-}
-
-void mClockScheduler::_init_logger()
-{
-  PerfCountersBuilder m(cct, "mclock-shard-queue-" + std::to_string(shard_id),
-                        l_mclock_first, l_mclock_last);
-
-  m.add_u64_counter(l_mclock_immediate_queue_len, "mclock_immediate_queue_len",
-                    "high_priority op count in mclock queue");
-  m.add_u64_counter(l_mclock_client_queue_len, "mclock_client_queue_len",
-                    "client type op count in mclock queue");
-  m.add_u64_counter(l_mclock_recovery_queue_len, "mclock_recovery_queue_len",
-                    "background_recovery type op count in mclock queue");
-  m.add_u64_counter(l_mclock_best_effort_queue_len, "mclock_best_effort_queue_len",
-                    "background_best_effort type op count in mclock queue");
-  m.add_u64_counter(l_mclock_all_type_queue_len, "mclock_all_type_queue_len",
-                    "all type op count in mclock queue");
-
-  logger = m.create_perf_counters();
-  cct->get_perfcounters_collection()->add(logger);
-
-  logger->set(l_mclock_immediate_queue_len, 0);
-  logger->set(l_mclock_client_queue_len, 0);
-  logger->set(l_mclock_recovery_queue_len, 0);
-  logger->set(l_mclock_best_effort_queue_len, 0);
-  logger->set(l_mclock_all_type_queue_len, 0);
-}
-
-/* ClientRegistry holds the dmclock::ClientInfo configuration parameters
- * (reservation (bytes/second), weight (unitless), limit (bytes/second))
- * for each IO class in the OSD (client, background_recovery,
- * background_best_effort).
- *
- * mclock expects limit and reservation to have units of <cost>/second
- * (bytes/second), but osd_mclock_scheduler_client_(lim|res) are provided
- * as ratios of the OSD's capacity.  We convert from the one to the other
- * using the capacity_per_shard parameter.
- *
- * Note, mclock profile information will already have been set as a default
- * for the osd_mclock_scheduler_client_* parameters prior to calling
- * update_from_config -- see set_config_defaults_from_profile().
- */
-void mClockScheduler::ClientRegistry::update_from_config(
-  const ConfigProxy &conf,
-  const double capacity_per_shard)
-{
-
-  auto get_res = [&](double res) {
-    if (res) {
-      return res * capacity_per_shard;
-    } else {
-      return default_min; // min reservation
-    }
-  };
-
-  auto get_lim = [&](double lim) {
-    if (lim) {
-      return lim * capacity_per_shard;
-    } else {
-      return default_max; // high limit
-    }
-  };
-
-  // Set external client infos
-  double res = conf.get_val<double>(
-    "osd_mclock_scheduler_client_res");
-  double lim = conf.get_val<double>(
-    "osd_mclock_scheduler_client_lim");
-  uint64_t wgt = conf.get_val<uint64_t>(
-    "osd_mclock_scheduler_client_wgt");
-  default_external_client_info.update(
-    get_res(res),
-    wgt,
-    get_lim(lim));
-
-  // Set background recovery client infos
-  res = conf.get_val<double>(
-    "osd_mclock_scheduler_background_recovery_res");
-  lim = conf.get_val<double>(
-    "osd_mclock_scheduler_background_recovery_lim");
-  wgt = conf.get_val<uint64_t>(
-    "osd_mclock_scheduler_background_recovery_wgt");
-  internal_client_infos[
-    static_cast<size_t>(op_scheduler_class::background_recovery)].update(
-      get_res(res),
-      wgt,
-      get_lim(lim));
-
-  // Set background best effort client infos
-  res = conf.get_val<double>(
-    "osd_mclock_scheduler_background_best_effort_res");
-  lim = conf.get_val<double>(
-    "osd_mclock_scheduler_background_best_effort_lim");
-  wgt = conf.get_val<uint64_t>(
-    "osd_mclock_scheduler_background_best_effort_wgt");
-  internal_client_infos[
-    static_cast<size_t>(op_scheduler_class::background_best_effort)].update(
-      get_res(res),
-      wgt,
-      get_lim(lim));
-}
-
-const dmc::ClientInfo *mClockScheduler::ClientRegistry::get_external_client(
-  const client_profile_id_t &client) const
-{
-  auto ret = external_client_infos.find(client);
-  if (ret == external_client_infos.end())
-    return &default_external_client_info;
-  else
-    return &(ret->second);
-}
-
-const dmc::ClientInfo *mClockScheduler::ClientRegistry::get_info(
-  const scheduler_id_t &id) const {
-  switch (id.class_id) {
-  case op_scheduler_class::immediate:
-    ceph_assert(0 == "Cannot schedule immediate");
-    return (dmc::ClientInfo*)nullptr;
-  case op_scheduler_class::client:
-    return get_external_client(id.client_profile_id);
-  default:
-    ceph_assert(static_cast<size_t>(id.class_id) < internal_client_infos.size());
-    return &internal_client_infos[static_cast<size_t>(id.class_id)];
-  }
-}
-
-void mClockScheduler::set_osd_capacity_params_from_config()
-{
-  uint64_t osd_bandwidth_capacity;
-  double osd_iop_capacity;
-
-  std::tie(osd_bandwidth_capacity, osd_iop_capacity) = [&, this] {
-    if (is_rotational) {
-      return std::make_tuple(
-        cct->_conf.get_val<Option::size_t>(
-          "osd_mclock_max_sequential_bandwidth_hdd"),
-        cct->_conf.get_val<double>("osd_mclock_max_capacity_iops_hdd"));
-    } else {
-      return std::make_tuple(
-        cct->_conf.get_val<Option::size_t>(
-          "osd_mclock_max_sequential_bandwidth_ssd"),
-        cct->_conf.get_val<double>("osd_mclock_max_capacity_iops_ssd"));
-    }
-  }();
-
-  osd_bandwidth_capacity = std::max<uint64_t>(1, osd_bandwidth_capacity);
-  osd_iop_capacity = std::max<double>(1.0, osd_iop_capacity);
-
-  osd_bandwidth_cost_per_io =
-    static_cast<double>(osd_bandwidth_capacity) / osd_iop_capacity;
-  osd_bandwidth_capacity_per_shard = static_cast<double>(osd_bandwidth_capacity)
-    / static_cast<double>(num_shards);
-
-  dout(1) << __func__ << ": osd_bandwidth_cost_per_io: "
-          << std::fixed << std::setprecision(2)
-          << osd_bandwidth_cost_per_io << " bytes/io"
-          << ", osd_bandwidth_capacity_per_shard "
-          << osd_bandwidth_capacity_per_shard << " bytes/second"
-          << dendl;
-}
-
-/**
- * profile_t
- *
- * mclock profile -- 3 params for each of 3 client classes
- * 0 (min): specifies no minimum reservation
- * 0 (max): specifies no upper limit
- */
-struct profile_t {
-  struct client_config_t {
-    double reservation;
-    uint64_t weight;
-    double limit;
-  };
-  client_config_t client;
-  client_config_t background_recovery;
-  client_config_t background_best_effort;
-};
-
-static std::ostream &operator<<(
-  std::ostream &lhs, const profile_t::client_config_t &rhs)
-{
-  return lhs << "{res: " << rhs.reservation
-             << ", wgt: " << rhs.weight
-             << ", lim: " << rhs.limit
-             << "}";
-}
-
-static std::ostream &operator<<(std::ostream &lhs, const profile_t &rhs)
-{
-  return lhs << "[client: " << rhs.client
-             << ", background_recovery: " << rhs.background_recovery
-             << ", background_best_effort: " << rhs.background_best_effort
-             << "]";
-}
-
-void mClockScheduler::set_config_defaults_from_profile()
-{
-  // Let only a single osd shard (id:0) set the profile configs
-  if (shard_id > 0) {
-    return;
-  }
-
-  /**
-   * high_client_ops
-   *
-   * Client Allocation:
-   *   reservation: 60% | weight: 2 | limit: 0 (max) |
-   * Background Recovery Allocation:
-   *   reservation: 40% | weight: 1 | limit: 0 (max) |
-   * Background Best Effort Allocation:
-   *   reservation: 0 (min) | weight: 1 | limit: 70% |
-   */
-  static constexpr profile_t high_client_ops_profile{
-    { .6, 2,  0 },
-    { .4, 1,  0 },
-    {  0, 1, .7 }
-  };
-
-  /**
-   * high_recovery_ops
-   *
-   * Client Allocation:
-   *   reservation: 30% | weight: 1 | limit: 0 (max) |
-   * Background Recovery Allocation:
-   *   reservation: 70% | weight: 2 | limit: 0 (max) |
-   * Background Best Effort Allocation:
-   *   reservation: 0 (min) | weight: 1 | limit: 0 (max) |
-   */
-  static constexpr profile_t high_recovery_ops_profile{
-    { .3, 1, 0 },
-    { .7, 2, 0 },
-    {  0, 1, 0 }
-  };
-
-  /**
-   * balanced
-   *
-   * Client Allocation:
-   *   reservation: 50% | weight: 1 | limit: 0 (max) |
-   * Background Recovery Allocation:
-   *   reservation: 50% | weight: 1 | limit: 0 (max) |
-   * Background Best Effort Allocation:
-   *   reservation: 0 (min) | weight: 1 | limit: 90% |
-   */
-  static constexpr profile_t balanced_profile{
-    { .5, 1, 0 },
-    { .5, 1, 0 },
-    {  0, 1, .9 }
-  };
-
-  const profile_t *profile = nullptr;
-  auto mclock_profile = cct->_conf.get_val<std::string>("osd_mclock_profile");
-  if (mclock_profile == "high_client_ops") {
-    profile = &high_client_ops_profile;
-    dout(10) << "Setting high_client_ops profile " << *profile << dendl;
-  } else if (mclock_profile == "high_recovery_ops") {
-    profile = &high_recovery_ops_profile;
-    dout(10) << "Setting high_recovery_ops profile " << *profile << dendl;
-  } else if (mclock_profile == "balanced") {
-    profile = &balanced_profile;
-    dout(10) << "Setting balanced profile " << *profile << dendl;
-  } else if (mclock_profile == "custom") {
-    dout(10) << "Profile set to custom, not setting defaults" << dendl;
-    return;
-  } else {
-    derr << "Invalid mclock profile: " << mclock_profile << dendl;
-    ceph_assert("Invalid choice of mclock profile" == 0);
-    return;
-  }
-  ceph_assert(nullptr != profile);
-
-  auto set_config = [&conf = cct->_conf](const char *key, auto val) {
-    conf.set_val_default(key, std::to_string(val));
-  };
-
-  set_config("osd_mclock_scheduler_client_res", profile->client.reservation);
-  set_config("osd_mclock_scheduler_client_wgt", profile->client.weight);
-  set_config("osd_mclock_scheduler_client_lim", profile->client.limit);
-
-  set_config(
-    "osd_mclock_scheduler_background_recovery_res",
-    profile->background_recovery.reservation);
-  set_config(
-    "osd_mclock_scheduler_background_recovery_wgt",
-    profile->background_recovery.weight);
-  set_config(
-    "osd_mclock_scheduler_background_recovery_lim",
-    profile->background_recovery.limit);
-
-  set_config(
-    "osd_mclock_scheduler_background_best_effort_res",
-    profile->background_best_effort.reservation);
-  set_config(
-    "osd_mclock_scheduler_background_best_effort_wgt",
-    profile->background_best_effort.weight);
-  set_config(
-    "osd_mclock_scheduler_background_best_effort_lim",
-    profile->background_best_effort.limit);
-
-  cct->_conf.apply_changes(nullptr);
-}
-
 uint32_t mClockScheduler::calc_scaled_cost(int item_cost)
 {
-  auto cost = static_cast<uint32_t>(
-    std::max<int>(
-      1, // ensure cost is non-zero and positive
-      item_cost));
-  auto cost_per_io = static_cast<uint32_t>(osd_bandwidth_cost_per_io);
-
-  return std::max<uint32_t>(cost, cost_per_io);
+  return mclock_conf.calc_scaled_cost(item_cost);
 }
 
 void mClockScheduler::update_configuration()
@@ -451,7 +86,7 @@ void mClockScheduler::enqueue(OpSchedulerItem&& item)
   unsigned priority = item.get_priority();
   
   // TODO: move this check into OpSchedulerItem, handle backwards compat
-  if (op_scheduler_class::immediate == id.class_id) {
+  if (SchedulerClass::immediate == id.class_id) {
     enqueue_high(immediate_class_priority, std::move(item));
   } else if (priority >= cutoff_priority) {
     enqueue_high(priority, std::move(item));
@@ -468,7 +103,7 @@ void mClockScheduler::enqueue(OpSchedulerItem&& item)
       std::move(item),
       id,
       cost);
-    _get_mclock_counter(id);
+    mclock_conf.get_mclock_counter(id);
   }
 
  dout(20) << __func__ << " client_count: " << scheduler.client_count()
@@ -489,7 +124,7 @@ void mClockScheduler::enqueue_front(OpSchedulerItem&& item)
   unsigned priority = item.get_priority();
   auto id = get_scheduler_id(item);
 
-  if (op_scheduler_class::immediate == id.class_id) {
+  if (SchedulerClass::immediate == id.class_id) {
     enqueue_high(immediate_class_priority, std::move(item), true);
   } else if (priority >= cutoff_priority) {
     enqueue_high(priority, std::move(item), true);
@@ -511,10 +146,10 @@ void mClockScheduler::enqueue_high(unsigned priority,
   }
 
   scheduler_id_t id = scheduler_id_t {
-    op_scheduler_class::immediate,
+    SchedulerClass::immediate,
     client_profile_id_t()
   };
-  _get_mclock_counter(id);
+  mclock_conf.get_mclock_counter(id);
 }
 
 WorkItem mClockScheduler::dequeue()
@@ -532,10 +167,10 @@ WorkItem mClockScheduler::dequeue()
     ceph_assert(std::get_if<OpSchedulerItem>(&ret));
 
     scheduler_id_t id = scheduler_id_t {
-      op_scheduler_class::immediate,
+      SchedulerClass::immediate,
       client_profile_id_t()
     };
-    _put_mclock_counter(id);
+    mclock_conf.put_mclock_counter(id);
     return ret;
   } else {
     mclock_queue_t::PullReq result = scheduler.pull_request();
@@ -549,7 +184,7 @@ WorkItem mClockScheduler::dequeue()
       ceph_assert(result.is_retn());
 
       auto &retn = result.get_retn();
-      _put_mclock_counter(retn.client);
+      mclock_conf.put_mclock_counter(retn.client);
       return std::move(*retn.request);
     }
   }
@@ -562,6 +197,7 @@ std::string mClockScheduler::display_queues() const
   return out.str();
 }
 
+
 std::vector<std::string> mClockScheduler::get_tracked_keys() const noexcept
 {
   return {
@@ -582,97 +218,17 @@ std::vector<std::string> mClockScheduler::get_tracked_keys() const noexcept
   };
 }
 
+
 void mClockScheduler::handle_conf_change(
   const ConfigProxy& conf,
   const std::set<std::string> &changed)
 {
-  if (changed.count("osd_mclock_max_capacity_iops_hdd") ||
-      changed.count("osd_mclock_max_capacity_iops_ssd")) {
-    set_osd_capacity_params_from_config();
-    client_registry.update_from_config(
-      conf, osd_bandwidth_capacity_per_shard);
-  }
-  if (changed.count("osd_mclock_max_sequential_bandwidth_hdd") ||
-      changed.count("osd_mclock_max_sequential_bandwidth_ssd")) {
-    set_osd_capacity_params_from_config();
-    client_registry.update_from_config(
-      conf, osd_bandwidth_capacity_per_shard);
-  }
-  if (changed.count("osd_mclock_profile")) {
-    set_config_defaults_from_profile();
-    client_registry.update_from_config(
-      conf, osd_bandwidth_capacity_per_shard);
-  }
-
-  auto get_changed_key = [&changed]() -> std::optional<std::string> {
-    static const std::vector<std::string> qos_params = {
-      "osd_mclock_scheduler_client_res",
-      "osd_mclock_scheduler_client_wgt",
-      "osd_mclock_scheduler_client_lim",
-      "osd_mclock_scheduler_background_recovery_res",
-      "osd_mclock_scheduler_background_recovery_wgt",
-      "osd_mclock_scheduler_background_recovery_lim",
-      "osd_mclock_scheduler_background_best_effort_res",
-      "osd_mclock_scheduler_background_best_effort_wgt",
-      "osd_mclock_scheduler_background_best_effort_lim"
-    };
-
-    for (auto &qp : qos_params) {
-      if (changed.count(qp)) {
-        return qp;
-      }
-    }
-    return std::nullopt;
-  };
-
-  if (auto key = get_changed_key(); key.has_value()) {
-    auto mclock_profile = cct->_conf.get_val<std::string>("osd_mclock_profile");
-    if (mclock_profile == "custom") {
-      client_registry.update_from_config(
-        conf, osd_bandwidth_capacity_per_shard);
-    } else {
-      // Attempt to change QoS parameter for a built-in profile. Restore the
-      // profile defaults by making one of the OSD shards remove the key from
-      // config monitor store. Note: monc is included in the check since the
-      // mock unit test currently doesn't initialize it.
-      if (shard_id == 0 && monc) {
-        static const std::vector<std::string> osds = {
-          "osd",
-          "osd." + std::to_string(whoami)
-        };
-
-        for (auto osd : osds) {
-          std::string cmd =
-            "{"
-              "\"prefix\": \"config rm\", "
-              "\"who\": \"" + osd + "\", "
-              "\"name\": \"" + *key + "\""
-            "}";
-          std::vector<std::string> vcmd{cmd};
-
-          dout(10) << __func__ << " Removing Key: " << *key
-                   << " for " << osd << " from Mon db" << dendl;
-          monc->start_mon_command(vcmd, {}, nullptr, nullptr, nullptr);
-        }
-      }
-    }
-    // Alternatively, the QoS parameter, if set ephemerally for this OSD via
-    // the 'daemon' or 'tell' interfaces must be removed.
-    if (!cct->_conf.rm_val(*key)) {
-      dout(10) << __func__ << " Restored " << *key << " to default" << dendl;
-      cct->_conf.apply_changes(nullptr);
-    }
-  }
+  mclock_conf.mclock_handle_conf_change(conf, changed);
 }
 
 mClockScheduler::~mClockScheduler()
 {
   cct->_conf.remove_observer(this);
-  if (logger) {
-    cct->get_perfcounters_collection()->remove(logger);
-    delete logger;
-    logger = nullptr;
-  }
 }
 
 }
index 8c86e0cefd3e9e19c2a02328540f30d08d3c252e..e94e037640a6d1a2c1a46bc04806ebee138be401 100644 (file)
 
 #include "osd/scheduler/OpScheduler.h"
 #include "common/config.h"
+#include "common/mclock_common.h"
 #include "common/ceph_context.h"
 #include "osd/scheduler/OpSchedulerItem.h"
 
 
-enum {
-  l_mclock_first = 15000,
-  l_mclock_immediate_queue_len,
-  l_mclock_client_queue_len,
-  l_mclock_recovery_queue_len,
-  l_mclock_best_effort_queue_len,
-  l_mclock_all_type_queue_len,
-  l_mclock_last,
-};
-
 namespace ceph::osd::scheduler {
 
-constexpr double default_min = 0.0;
-constexpr double default_max = std::numeric_limits<double>::is_iec559 ?
-  std::numeric_limits<double>::infinity() :
-  std::numeric_limits<double>::max();
-
-/**
- * client_profile_id_t
- *
- * client_id - global id (client.####) for client QoS
- * profile_id - id generated by client's QoS profile
- *
- * Currently (Reef and below), both members are set to
- * 0 which ensures that all external clients share the
- * mClock profile allocated reservation and limit
- * bandwidth.
- *
- * Note: Post Reef, both members will be set to non-zero
- * values when the distributed feature of the mClock
- * algorithm is utilized.
- */
-struct client_profile_id_t {
-  uint64_t client_id = 0;
-  uint64_t profile_id = 0;
-
-  client_profile_id_t(uint64_t _client_id, uint64_t _profile_id) :
-    client_id(_client_id),
-    profile_id(_profile_id) {}
-
-  client_profile_id_t() = default;
-
-  auto operator<=>(const client_profile_id_t&) const = default;
-  friend std::ostream& operator<<(std::ostream& out,
-                                  const client_profile_id_t& client_profile) {
-    out << " client_id: " << client_profile.client_id
-        << " profile_id: " << client_profile.profile_id;
-    return out;
-  }
-};
-
-struct scheduler_id_t {
-  op_scheduler_class class_id;
-  client_profile_id_t client_profile_id;
-
-  auto operator<=>(const scheduler_id_t&) const = default;
-  friend std::ostream& operator<<(std::ostream& out,
-                                  const scheduler_id_t& sched_id) {
-    out << "{ class_id: " << sched_id.class_id
-        << sched_id.client_profile_id;
-    return out << " }";
-  }
-};
-
 /**
  * Scheduler implementation based on mclock.
  *
@@ -102,87 +41,11 @@ struct scheduler_id_t {
 class mClockScheduler : public OpScheduler, md_config_obs_t {
 
   CephContext *cct;
-  const int whoami;
-  const uint32_t num_shards;
-  const int shard_id;
-  const bool is_rotational;
   const unsigned cutoff_priority;
   MonClient *monc;
-  PerfCounters *logger;
-
-  /**
-   * osd_bandwidth_cost_per_io
-   *
-   * mClock expects all queued items to have a uniform expression of
-   * "cost".  However, IO devices generally have quite different capacity
-   * for sequential IO vs small random IO.  This implementation handles this
-   * by expressing all costs as a number of sequential bytes written adding
-   * additional cost for each random IO equal to osd_bandwidth_cost_per_io.
-   *
-   * Thus, an IO operation requiring a total of <size> bytes to be written
-   * accross <iops> different locations will have a cost of
-   * <size> + (osd_bandwidth_cost_per_io * <iops>) bytes.
-   *
-   * Set in set_osd_capacity_params_from_config in the constructor and upon
-   * config change.
-   *
-   * Has units bytes/io.
-   */
-  double osd_bandwidth_cost_per_io;
-
-  /**
-   * osd_bandwidth_capacity_per_shard
-   *
-   * mClock expects reservation and limit paramters to be expressed in units
-   * of cost/second -- which means bytes/second for this implementation.
-   *
-   * Rather than expecting users to compute appropriate limit and reservation
-   * values for each class of OSDs in their cluster, we instead express
-   * reservation and limit paramaters as ratios of the OSD's maxmimum capacity.
-   * osd_bandwidth_capacity_per_shard is that capacity divided by the number
-   * of shards.
-   *
-   * Set in set_osd_capacity_params_from_config in the constructor and upon
-   * config change.
-   *
-   * This value gets passed to ClientRegistry::update_from_config in order
-   * to resolve the full reservaiton and limit parameters for mclock from
-   * the configured ratios.
-   *
-   * Has units bytes/second.
-   */
-  double osd_bandwidth_capacity_per_shard;
-
-  class ClientRegistry {
-    std::array<
-      crimson::dmclock::ClientInfo,
-      static_cast<size_t>(op_scheduler_class::immediate)
-    > internal_client_infos = {
-      // Placeholder, gets replaced with configured values
-      crimson::dmclock::ClientInfo(1, 1, 1),
-      crimson::dmclock::ClientInfo(1, 1, 1)
-    };
-
-    crimson::dmclock::ClientInfo default_external_client_info = {1, 1, 1};
-    std::map<client_profile_id_t,
-            crimson::dmclock::ClientInfo> external_client_infos;
-    const crimson::dmclock::ClientInfo *get_external_client(
-      const client_profile_id_t &client) const;
-  public:
-    /**
-     * update_from_config
-     *
-     * Sets the mclock paramaters (reservation, weight, and limit)
-     * for each class of IO (background_recovery, background_best_effort,
-     * and client).
-     */
-    void update_from_config(
-      const ConfigProxy &conf,
-      double capacity_per_shard);
-    const crimson::dmclock::ClientInfo *get_info(
-      const scheduler_id_t &id) const;
-  } client_registry;
 
+  ClientRegistry client_registry;
+  MclockConfig mclock_conf;
   using mclock_queue_t = crimson::dmclock::PullPriorityQueue<
     scheduler_id_t,
     OpSchedulerItem,
@@ -210,23 +73,6 @@ class mClockScheduler : public OpScheduler, md_config_obs_t {
     };
   }
 
-  /**
-   * set_osd_capacity_params_from_config
-   *
-   * mClockScheduler uses two parameters, osd_bandwidth_cost_per_io
-   * and osd_bandwidth_capacity_per_shard, internally.  These two
-   * parameters are derived from config parameters
-   * osd_mclock_max_capacity_iops_(hdd|ssd) and
-   * osd_mclock_max_sequential_bandwidth_(hdd|ssd) as well as num_shards.
-   * Invoking set_osd_capacity_params_from_config() resets those derived
-   * params based on the current config and should be invoked any time they
-   * are modified as well as in the constructor.  See handle_conf_change().
-   */
-  void set_osd_capacity_params_from_config();
-
-  // Set the mclock related config params based on the profile
-  void set_config_defaults_from_profile();
-
 public: 
   template<typename Rep, typename Per>
   mClockScheduler(
@@ -238,15 +84,12 @@ public:
     MonClient *monc,
     bool init_perfcounter=true)
     : cct(cct),
-      whoami(whoami),
-      num_shards(num_shards),
-      shard_id(shard_id),
-      is_rotational(is_rotational),
       cutoff_priority(cutoff_priority),
       monc(monc),
-      logger(nullptr),
+      mclock_conf(cct, client_registry, monc, num_shards,
+                 is_rotational, shard_id, whoami),
       scheduler(
-       std::bind(&mClockScheduler::ClientRegistry::get_info,
+       std::bind(&ClientRegistry::get_info,
                  &client_registry,
                  std::placeholders::_1),
        idle_age, erase_age, check_time,
@@ -255,13 +98,13 @@ public:
   {
     cct->_conf.add_observer(this);
     ceph_assert(num_shards > 0);
-    set_osd_capacity_params_from_config();
-    set_config_defaults_from_profile();
+    mclock_conf.set_osd_capacity_params_from_config();
+    mclock_conf.set_config_defaults_from_profile();
     client_registry.update_from_config(
-      cct->_conf, osd_bandwidth_capacity_per_shard);
+      cct->_conf, mclock_conf.get_capacity_per_shard());
 
     if (init_perfcounter) {
-      _init_logger();
+      mclock_conf.init_logger();
     }
   }
   mClockScheduler(
@@ -318,14 +161,11 @@ public:
                          const std::set<std::string> &changed) final;
 
   double get_cost_per_io() const {
-    return osd_bandwidth_cost_per_io;
+    return mclock_conf.get_cost_per_io();
   }
 private:
   // Enqueue the op to the high priority queue
   void enqueue_high(unsigned prio, OpSchedulerItem &&item, bool front = false);
-  void _init_logger();
-  void _get_mclock_counter(scheduler_id_t id);
-  void _put_mclock_counter(scheduler_id_t id);
 };
 
 }
index db016b2f9bdc2569173965e715ebea5897f3811c..e283a2b3bd2675cb679d56c84e22bf2589354337 100644 (file)
@@ -7,6 +7,7 @@
 #include "global/global_context.h"
 #include "global/global_init.h"
 #include "common/common_init.h"
+#include "common/mclock_common.h"
 
 #include "osd/scheduler/mClockScheduler.h"
 #include "osd/scheduler/OpSchedulerItem.h"
@@ -59,14 +60,14 @@ public:
   {}
 
   struct MockDmclockItem : public PGOpQueueable {
-    op_scheduler_class scheduler_class;
+    SchedulerClass scheduler_class;
 
-    MockDmclockItem(op_scheduler_class _scheduler_class) :
+    MockDmclockItem(SchedulerClass _scheduler_class) :
       PGOpQueueable(spg_t()),
       scheduler_class(_scheduler_class) {}
 
     MockDmclockItem()
-      : MockDmclockItem(op_scheduler_class::background_best_effort) {}
+      : MockDmclockItem(SchedulerClass::background_best_effort) {}
 
     ostream &print(ostream &rhs) const final { return rhs; }
 
@@ -78,7 +79,7 @@ public:
       return std::nullopt;
     }
 
-    op_scheduler_class get_scheduler_class() const final {
+    SchedulerClass get_scheduler_class() const final {
       return scheduler_class;
     }
 
@@ -118,7 +119,7 @@ TEST_F(mClockSchedulerTest, TestEmpty) {
   ASSERT_TRUE(q.empty());
 
   for (unsigned i = 100; i < 105; i+=2) {
-    q.enqueue(create_item(i, client1, op_scheduler_class::client));
+    q.enqueue(create_item(i, client1, SchedulerClass::client));
     std::this_thread::sleep_for(std::chrono::microseconds(1));
   }
 
@@ -151,7 +152,7 @@ TEST_F(mClockSchedulerTest, TestSingleClientOrderedEnqueueDequeue) {
   ASSERT_TRUE(q.empty());
 
   for (unsigned i = 100; i < 105; ++i) {
-    q.enqueue(create_item(i, client1, op_scheduler_class::client));
+    q.enqueue(create_item(i, client1, SchedulerClass::client));
     std::this_thread::sleep_for(std::chrono::microseconds(1));
   }
 
@@ -175,7 +176,7 @@ TEST_F(mClockSchedulerTest, TestMultiClientOrderedEnqueueDequeue) {
   const unsigned NUM = 1000;
   for (unsigned i = 0; i < NUM; ++i) {
     for (auto &&c: {client1, client2, client3}) {
-      q.enqueue(create_item(i, c, op_scheduler_class::client));
+      q.enqueue(create_item(i, c, SchedulerClass::client));
       std::this_thread::sleep_for(std::chrono::microseconds(1));
     }
   }
@@ -199,7 +200,7 @@ TEST_F(mClockSchedulerTest, TestMultiClientOrderedEnqueueDequeue) {
 TEST_F(mClockSchedulerTest, TestHighPriorityQueueEnqueueDequeue) {
   ASSERT_TRUE(q.empty());
   for (unsigned i = 200; i < 205; ++i) {
-    q.enqueue(create_high_prio_item(i, i, client1, op_scheduler_class::client));
+    q.enqueue(create_high_prio_item(i, i, client1, SchedulerClass::client));
     std::this_thread::sleep_for(std::chrono::milliseconds(1));
   }
 
@@ -228,19 +229,19 @@ TEST_F(mClockSchedulerTest, TestAllQueuesEnqueueDequeue) {
 
   // Insert ops into the mClock queue
   for (unsigned i = 100; i < 102; ++i) {
-    q.enqueue(create_item(i, client1, op_scheduler_class::client));
+    q.enqueue(create_item(i, client1, SchedulerClass::client));
     std::this_thread::sleep_for(std::chrono::microseconds(1));
   }
 
   // Insert Immediate ops
   for (unsigned i = 103; i < 105; ++i) {
-    q.enqueue(create_item(i, client1, op_scheduler_class::immediate));
+    q.enqueue(create_item(i, client1, SchedulerClass::immediate));
     std::this_thread::sleep_for(std::chrono::microseconds(1));
   }
 
   // Insert ops into the high queue
   for (unsigned i = 200; i < 202; ++i) {
-    q.enqueue(create_high_prio_item(i, i, client1, op_scheduler_class::client));
+    q.enqueue(create_high_prio_item(i, i, client1, SchedulerClass::client));
     std::this_thread::sleep_for(std::chrono::milliseconds(1));
   }
 
@@ -278,11 +279,11 @@ TEST_F(mClockSchedulerTest, TestSlowDequeue) {
   // Insert ops into the mClock queue
   unsigned i = 0;
   for (; i < 100; ++i) {
-    q.enqueue(create_item(i, client1, op_scheduler_class::background_best_effort));
+    q.enqueue(create_item(i, client1, SchedulerClass::background_best_effort));
     std::this_thread::sleep_for(5ms);
   }
   for (; i < 200; ++i) {
-    q.enqueue(create_item(i, client2, op_scheduler_class::client));
+    q.enqueue(create_item(i, client2, SchedulerClass::client));
     std::this_thread::sleep_for(5ms);
   }