]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
librbd: optionally exclude ops from qos
authorchenerqi <chenerqi@gmail.com>
Mon, 28 Dec 2020 13:03:13 +0000 (21:03 +0800)
committerchenerqi <chenerqi@gmail.com>
Fri, 22 Jan 2021 08:32:11 +0000 (16:32 +0800)
Optionally exclude ops from qos, set by a set of comma-delimated keys,
for example discards w/mkfs (instead of disabling it) in combination
with QoS BPS limits, exclude discard ops can control cluster storage
capacity usage and client network bandwith at the same time.

Signed-off-by: Erqi Chen <chenerqi@kuaishou.com>
Signed-off-by: Shuai Ni <nishuai@kuaishou.com>
15 files changed:
src/CMakeLists.txt
src/common/options.cc
src/crimson/CMakeLists.txt
src/librbd/CMakeLists.txt
src/librbd/ImageCtx.cc
src/librbd/io/ImageDispatcher.cc
src/librbd/io/ImageDispatcher.h
src/librbd/io/ImageDispatcherInterface.h
src/librbd/io/IoOperations.cc [new file with mode: 0644]
src/librbd/io/IoOperations.h [new file with mode: 0644]
src/librbd/io/QosImageDispatch.cc
src/librbd/io/QosImageDispatch.h
src/librbd/io/Types.h
src/test/common/test_context.cc
src/test/librbd/mock/io/MockImageDispatcher.h

index d5ce19c54b753f1d081dba64fa6f72c03ab50282..05235b691852c19a46ce28f6db3724323abfdbd8 100644 (file)
@@ -385,6 +385,7 @@ set(libcommon_files
   osdc/Objecter.cc
   osdc/error_code.cc
   librbd/Features.cc
+  librbd/io/IoOperations.cc
   ${mds_files})
 set_source_files_properties(ceph_ver.c
   APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_BINARY_DIR}/src/include/ceph_ver.h)
index 367e52ee0dba2e127731c9875fb6f9e6c6813c83..afa64f661b36c3c0431d7498d46b952747924aee 100644 (file)
@@ -15,8 +15,9 @@
 // Definitions for enums
 #include "common/perf_counters.h"
 
-// rbd feature validation
+// rbd feature and io operation validation
 #include "librbd/Features.h"
+#include "librbd/io/IoOperations.h"
 
 using std::ostream;
 using std::ostringstream;
@@ -7683,6 +7684,29 @@ static std::vector<Option> get_rbd_options() {
     .set_min(1)
     .set_description("minimum schedule tick (in milliseconds) for QoS"),
 
+    Option("rbd_qos_exclude_ops", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+    .set_default("")
+    .set_description("optionally exclude ops from QoS")
+    .set_long_description(
+        "Optionally exclude ops from QoS. This setting accepts either "
+        "an integer bitmask value or comma-delimited string of op "
+        "names. This setting is always internally stored as an integer "
+        "bitmask value. The mapping between op bitmask value and op "
+        "name is as follows: +1 -> read, +2 -> write, +4 -> discard, "
+        "+8 -> write_same, +16 -> compare_and_write")
+    .set_flag(Option::FLAG_RUNTIME)
+    .set_validator([](std::string *value, std::string *error_message) {
+        ostringstream ss;
+        uint64_t exclude_ops = librbd::io::rbd_io_operations_from_string(*value, &ss);
+        // Leave this in integer form to avoid breaking Cinder.  Someday
+        // we would like to present this in string form instead...
+        *value = stringify(exclude_ops);
+        if (ss.str().size()) {
+          return -EINVAL;
+        }
+        return 0;
+    }),
+
     Option("rbd_discard_on_zeroed_write_same", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
     .set_default(true)
     .set_description("discard data on zeroed write same instead of writing zero"),
index f714aa9fbade36d44c44567b661100e484b982d0..c08cf04284f4cdef1b703b68d1275664ba467455 100644 (file)
@@ -95,6 +95,7 @@ add_library(crimson-common STATIC
   ${PROJECT_SOURCE_DIR}/src/global/global_context.cc
   ${PROJECT_SOURCE_DIR}/src/global/pidfile.cc
   ${PROJECT_SOURCE_DIR}/src/librbd/Features.cc
+  ${PROJECT_SOURCE_DIR}/src/librbd/io/IoOperations.cc
   ${PROJECT_SOURCE_DIR}/src/log/Log.cc
   ${PROJECT_SOURCE_DIR}/src/mgr/ServiceMap.cc
   ${PROJECT_SOURCE_DIR}/src/mds/inode_backtrace.cc
index dfd7940a4e9623a83c2710fb132d9517a7ace912..5cea46ebc616667f37dd42a582c231c59c528ee0 100644 (file)
@@ -106,6 +106,7 @@ set(librbd_internal_srcs
   io/Types.cc
   io/Utils.cc
   io/WriteBlockImageDispatch.cc
+  io/IoOperations.cc
   journal/CreateRequest.cc
   journal/DemoteRequest.cc
   journal/ObjectDispatch.cc
index c1856a0bf87b1921e022b41326d032aa36064e3f..7122f44fd351e7cbd054a3b484c3eede95dbd3b2 100644 (file)
@@ -34,6 +34,7 @@
 #include "librbd/io/ImageDispatcher.h"
 #include "librbd/io/ObjectDispatcher.h"
 #include "librbd/io/QosImageDispatch.h"
+#include "librbd/io/IoOperations.h"
 #include "librbd/journal/StandardPolicy.h"
 #include "librbd/operation/ResizeRequest.h"
 
@@ -843,6 +844,9 @@ librados::IoCtx duplicate_io_ctx(librados::IoCtx& io_ctx) {
       config.get_val<uint64_t>("rbd_qos_write_bps_limit"),
       config.get_val<uint64_t>("rbd_qos_write_bps_burst"),
       config.get_val<uint64_t>("rbd_qos_write_bps_burst_seconds"));
+    io_image_dispatcher->apply_qos_exclude_ops(
+      librbd::io::rbd_io_operations_from_string(
+        config.get_val<std::string>("rbd_qos_exclude_ops"), nullptr));
 
     if (!disable_zero_copy &&
         config.get_val<bool>("rbd_disable_zero_copy_writes")) {
index a9c1c985522decf4670cc0e9765f1bda7612ab5d..9742518d2a7d75023d3459d5e3c45bffa8276262 100644 (file)
@@ -235,6 +235,11 @@ void ImageDispatcher<I>::apply_qos_limit(uint64_t flag, uint64_t limit,
   m_qos_image_dispatch->apply_qos_limit(flag, limit, burst, burst_seconds);
 }
 
+template <typename I>
+void ImageDispatcher<I>::apply_qos_exclude_ops(uint64_t exclude_ops) {
+  m_qos_image_dispatch->apply_qos_exclude_ops(exclude_ops);
+}
+
 template <typename I>
 bool ImageDispatcher<I>::writes_blocked() const {
   return m_write_block_dispatch->writes_blocked();
index d1176ae050e64a2f28c3cdd8a26189549b07284e..488fdff9a59722741f6a425f827a8fb13a40cb93 100644 (file)
@@ -37,6 +37,7 @@ public:
   void apply_qos_schedule_tick_min(uint64_t tick) override;
   void apply_qos_limit(uint64_t flag, uint64_t limit, uint64_t burst,
                        uint64_t burst_seconds) override;
+  void apply_qos_exclude_ops(uint64_t exclude_ops) override;
 
   bool writes_blocked() const override;
   int block_writes() override;
index f6b572c5d9e07aea84cb6ff33ebf345524660972..6d5828b9161c63e065710844ea0c029af2291bfa 100644 (file)
@@ -20,6 +20,7 @@ public:
   virtual void apply_qos_schedule_tick_min(uint64_t tick) = 0;
   virtual void apply_qos_limit(uint64_t flag, uint64_t limit,
                                uint64_t burst, uint64_t burst_seconds) = 0;
+  virtual void apply_qos_exclude_ops(uint64_t exclude_ops) = 0;
 
   virtual bool writes_blocked() const = 0;
   virtual int block_writes() = 0;
diff --git a/src/librbd/io/IoOperations.cc b/src/librbd/io/IoOperations.cc
new file mode 100644 (file)
index 0000000..7db7e7a
--- /dev/null
@@ -0,0 +1,101 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include "librbd/io/Types.h"
+#include "librbd/io/IoOperations.h"
+
+#include <map>
+#include <vector>
+
+namespace librbd {
+namespace io {
+
+#define RBD_IO_OPERATION_NAME_READ                 "read"
+#define RBD_IO_OPERATION_NAME_WRITE                "write"
+#define RBD_IO_OPERATION_NAME_DISCARD              "discard"
+#define RBD_IO_OPERATION_NAME_WRITE_SAME           "write_same"
+#define RBD_IO_OPERATION_NAME_COMPARE_AND_WRITE    "compare_and_write"
+
+static const std::map<std::string, uint64_t> RBD_IO_OPERATION_MAP = {
+  {RBD_IO_OPERATION_NAME_READ, RBD_IO_OPERATION_READ},
+  {RBD_IO_OPERATION_NAME_WRITE, RBD_IO_OPERATION_WRITE},
+  {RBD_IO_OPERATION_NAME_DISCARD, RBD_IO_OPERATION_DISCARD},
+  {RBD_IO_OPERATION_NAME_WRITE_SAME, RBD_IO_OPERATION_WRITE_SAME},
+  {RBD_IO_OPERATION_NAME_COMPARE_AND_WRITE, RBD_IO_OPERATION_COMPARE_AND_WRITE},
+};
+static_assert((RBD_IO_OPERATION_COMPARE_AND_WRITE << 1) > RBD_IO_OPERATIONS_ALL,
+                         "new RBD io operation added");
+
+std::string rbd_io_operations_to_string(uint64_t operations,
+                                        std::ostream *err)
+{
+  std::string r;
+  for (auto& i : RBD_IO_OPERATION_MAP) {
+    if (operations & i.second) {
+      if (!r.empty()) {
+      r += ",";
+      }
+      r += i.first;
+      operations &= ~i.second;
+    }
+  }
+  if (err && operations) {
+    *err << "ignoring unknown io operation mask 0x"
+        << std::hex << operations << std::dec;
+  }
+  return r;
+}
+
+uint64_t rbd_io_operations_from_string(const std::string& orig_value,
+                                      std::ostream *err)
+{
+  uint64_t operations = 0;
+  std::string value = orig_value;
+  boost::trim(value);
+
+  // empty string means default operations
+  if (!value.size()) {
+    return RBD_IO_OPERATIONS_DEFAULT;
+  }
+
+  try {
+    // numeric?
+    operations = boost::lexical_cast<uint64_t>(value);
+
+    // drop unrecognized bits
+    uint64_t unsupported_operations = (operations & ~RBD_IO_OPERATIONS_ALL);
+    if (unsupported_operations != 0ull) {
+      operations &= RBD_IO_OPERATIONS_ALL;
+      if (err) {
+       *err << "ignoring unknown operation mask 0x"
+             << std::hex << unsupported_operations << std::dec;
+      }
+    }
+  } catch (boost::bad_lexical_cast&) {
+    // operation name list?
+    bool errors = false;
+    std::vector<std::string> operation_names;
+    boost::split(operation_names, value, boost::is_any_of(","));
+    for (auto operation_name: operation_names) {
+      boost::trim(operation_name);
+      auto operation_it = RBD_IO_OPERATION_MAP.find(operation_name);
+      if (operation_it != RBD_IO_OPERATION_MAP.end()) {
+       operations += operation_it->second;
+      } else if (err) {
+       if (errors) {
+         *err << ", ";
+       } else {
+         errors = true;
+       }
+       *err << "ignoring unknown operation " << operation_name;
+      }
+    }
+  }
+  return operations;
+}
+
+} // namespace io
+} // namespace librbd
diff --git a/src/librbd/io/IoOperations.h b/src/librbd/io/IoOperations.h
new file mode 100644 (file)
index 0000000..93d3ef4
--- /dev/null
@@ -0,0 +1,18 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <string>
+#include <ostream>
+
+namespace librbd {
+namespace io {
+
+  std::string rbd_io_operations_to_string(uint64_t ops,
+                                          std::ostream *err);
+  uint64_t rbd_io_operations_from_string(const std::string& value,
+                                         std::ostream *err);
+
+} // namespace io
+} // namespace librbd
index 9ca88ac1916aa0c625ffd6526818f29c0d33b2a6..324c670fe1fb44841ea4c2df1464db102250908b 100644 (file)
@@ -113,6 +113,11 @@ void QosImageDispatch<I>::apply_qos_limit(uint64_t flag, uint64_t limit,
   }
 }
 
+template <typename I>
+void QosImageDispatch<I>::apply_qos_exclude_ops(uint64_t exclude_ops) {
+  m_qos_exclude_ops = exclude_ops;
+}
+
 template <typename I>
 bool QosImageDispatch<I>::read(
     AioCompletion* aio_comp, Extents &&image_extents, ReadResult &&read_result,
@@ -125,6 +130,10 @@ bool QosImageDispatch<I>::read(
   ldout(cct, 20) << "tid=" << tid << ", image_extents=" << image_extents
                  << dendl;
 
+  if (m_qos_exclude_ops & RBD_IO_OPERATION_READ) {
+    return false;
+  }
+
   if (needs_throttle(true, image_extents, tid, image_dispatch_flags,
                      dispatch_result, on_finish, on_dispatched)) {
     return true;
@@ -144,6 +153,10 @@ bool QosImageDispatch<I>::write(
   ldout(cct, 20) << "tid=" << tid << ", image_extents=" << image_extents
                  << dendl;
 
+  if (m_qos_exclude_ops & RBD_IO_OPERATION_WRITE) {
+    return false;
+  }
+
   if (needs_throttle(false, image_extents, tid, image_dispatch_flags,
                      dispatch_result, on_finish, on_dispatched)) {
     return true;
@@ -164,6 +177,10 @@ bool QosImageDispatch<I>::discard(
   ldout(cct, 20) << "tid=" << tid << ", image_extents=" << image_extents
                  << dendl;
 
+  if (m_qos_exclude_ops & RBD_IO_OPERATION_DISCARD) {
+    return false;
+  }
+
   if (needs_throttle(false, image_extents, tid, image_dispatch_flags,
                      dispatch_result, on_finish, on_dispatched)) {
     return true;
@@ -183,6 +200,10 @@ bool QosImageDispatch<I>::write_same(
   ldout(cct, 20) << "tid=" << tid << ", image_extents=" << image_extents
                  << dendl;
 
+  if (m_qos_exclude_ops & RBD_IO_OPERATION_WRITE_SAME) {
+    return false;
+  }
+
   if (needs_throttle(false, image_extents, tid, image_dispatch_flags,
                      dispatch_result, on_finish, on_dispatched)) {
     return true;
@@ -203,6 +224,10 @@ bool QosImageDispatch<I>::compare_and_write(
   ldout(cct, 20) << "tid=" << tid << ", image_extents=" << image_extents
                  << dendl;
 
+  if (m_qos_exclude_ops & RBD_IO_OPERATION_COMPARE_AND_WRITE) {
+    return false;
+  }
+
   if (needs_throttle(false, image_extents, tid, image_dispatch_flags,
                      dispatch_result, on_finish, on_dispatched)) {
     return true;
index baf16da02be0e9c73d5b9c8c95982cdbec323fc4..a1264cf828fe86ef72d105b59101fb3d9a775cbf 100644 (file)
@@ -49,6 +49,7 @@ public:
   void apply_qos_schedule_tick_min(uint64_t tick);
   void apply_qos_limit(uint64_t flag, uint64_t limit, uint64_t burst,
                        uint64_t burst_seconds);
+  void apply_qos_exclude_ops(uint64_t exclude_ops);
 
   bool read(
       AioCompletion* aio_comp, Extents &&image_extents,
@@ -109,6 +110,7 @@ private:
 
   std::list<std::pair<uint64_t, TokenBucketThrottle*> > m_throttles;
   uint64_t m_qos_enabled_flag = 0;
+  uint64_t m_qos_exclude_ops = 0;
 
   FlushTracker<ImageCtxT>* m_flush_tracker;
 
index 8d91c55158c6bc9f3f63a9df44bf4b88f634c744..030f0814a66debb944c981319ea4fc57738c0232 100644 (file)
@@ -97,6 +97,21 @@ enum {
     IMAGE_DISPATCH_FLAG_QOS_IOPS_MASK),
 };
 
+enum {
+  RBD_IO_OPERATIONS_DEFAULT             = 0,
+  RBD_IO_OPERATION_READ                 = 1 << 0,
+  RBD_IO_OPERATION_WRITE                = 1 << 1,
+  RBD_IO_OPERATION_DISCARD              = 1 << 2,
+  RBD_IO_OPERATION_WRITE_SAME           = 1 << 3,
+  RBD_IO_OPERATION_COMPARE_AND_WRITE    = 1 << 4,
+  RBD_IO_OPERATIONS_ALL                 = (
+    RBD_IO_OPERATION_READ |
+    RBD_IO_OPERATION_WRITE |
+    RBD_IO_OPERATION_DISCARD |
+    RBD_IO_OPERATION_WRITE_SAME |
+    RBD_IO_OPERATION_COMPARE_AND_WRITE)
+};
+
 enum ImageExtentsMapType {
     IMAGE_EXTENTS_MAP_TYPE_LOGICAL_TO_PHYSICAL,
     IMAGE_EXTENTS_MAP_TYPE_PHYSICAL_TO_LOGICAL,
index 6b00f61c17bcf4c3ea848f999e4184908aac8233..8afdaec1f107f10e491d61962d6e2a626bb4366c 100644 (file)
@@ -85,7 +85,7 @@ TEST(CephContext, do_command)
     cct->do_command("config diff get", cmdmap, f.get(), ss, &out);
     f->flush(out);
     string s(out.c_str(), out.length());
-    EXPECT_EQ("<config_diff_get><diff><key><default></default><override>" + value + "</override><final>value</final></key><rbd_default_features><default>61</default><final>61</final></rbd_default_features></diff></config_diff_get>", s);
+    EXPECT_EQ("<config_diff_get><diff><key><default></default><override>" + value + "</override><final>value</final></key><rbd_default_features><default>61</default><final>61</final></rbd_default_features><rbd_qos_exclude_ops><default>0</default><final>0</final></rbd_qos_exclude_ops></diff></config_diff_get>", s);
   }
   cct->put();
 }
index bd1c962e8d0c544506c6bb5dacbd8efccf15c208..4cd34f79901763f8534f3f3d85f7d205e7e30bf2 100644 (file)
@@ -31,6 +31,7 @@ public:
 
   MOCK_METHOD1(apply_qos_schedule_tick_min, void(uint64_t));
   MOCK_METHOD4(apply_qos_limit, void(uint64_t, uint64_t, uint64_t, uint64_t));
+  MOCK_METHOD1(apply_qos_exclude_ops, void(uint64_t));
 
   MOCK_CONST_METHOD0(writes_blocked, bool());
   MOCK_METHOD0(block_writes, int());