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)
// 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;
.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"),
${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
io/Types.cc
io/Utils.cc
io/WriteBlockImageDispatch.cc
+ io/IoOperations.cc
journal/CreateRequest.cc
journal/DemoteRequest.cc
journal/ObjectDispatch.cc
#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"
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")) {
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();
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;
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;
--- /dev/null
+// -*- 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
--- /dev/null
+// -*- 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
}
}
+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,
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;
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;
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;
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;
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;
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,
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;
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,
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();
}
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());