template std::unique_ptr<AdminSocketHook>
make_asok_hook<DumpInFlightOpsHook>(const crimson::osd::OSDOperationRegistry& op_registry);
+
+class DumpHistoricOpsHook : public AdminSocketHook {
+public:
+ explicit DumpHistoricOpsHook(const crimson::osd::OSDOperationRegistry& op_registry) :
+ AdminSocketHook{"dump_historic_ops", "", "show recent ops"},
+ op_registry(op_registry)
+ {}
+ seastar::future<tell_result_t> call(const cmdmap_t&,
+ std::string_view format,
+ ceph::bufferlist&& input) const final
+ {
+ unique_ptr<Formatter> f{Formatter::create(format, "json-pretty", "json-pretty")};
+ f->open_object_section("historic_ops");
+ op_registry.dump_historic_client_requests(f.get());
+ f->close_section();
+ f->dump_int("num_ops", 0);
+ return seastar::make_ready_future<tell_result_t>(std::move(f));
+ }
+private:
+ const crimson::osd::OSDOperationRegistry& op_registry;
+};
+template std::unique_ptr<AdminSocketHook>
+make_asok_hook<DumpHistoricOpsHook>(const crimson::osd::OSDOperationRegistry& op_registry);
+
} // namespace crimson::admin
class OsdStatusHook;
class SendBeaconHook;
class DumpInFlightOpsHook;
+class DumpHistoricOpsHook;
template<class Hook, class... Args>
std::unique_ptr<AdminSocketHook> make_asok_hook(Args&&... args);
protected:
virtual void do_register(Operation *op) = 0;
virtual bool registries_empty() const = 0;
+ virtual void do_stop() = 0;
public:
using op_list = boost::intrusive::list<
}
seastar::future<> stop() {
+ do_stop();
shutdown_timer.set_callback([this] {
if (registries_empty()) {
shutdown.set_value();
REGISTRY_INDEX < std::tuple_size<decltype(registries)>::value);
return registries[REGISTRY_INDEX];
}
+
+ template <size_t REGISTRY_INDEX>
+ op_list& get_registry() {
+ static_assert(
+ REGISTRY_INDEX < std::tuple_size<decltype(registries)>::value);
+ return registries[REGISTRY_INDEX];
+ }
};
class PipelineExitBarrierI {
object_context.cc
ops_executer.cc
osd_operation.cc
+ osd_operation_external_tracking.cc
osd_operations/client_request.cc
osd_operations/client_request_common.cc
osd_operations/compound_peering_request.cc
// ops commands
asok->register_command(make_asok_hook<DumpInFlightOpsHook>(
std::as_const(get_shard_services().registry)));
+ asok->register_command(make_asok_hook<DumpHistoricOpsHook>(
+ std::as_const(get_shard_services().registry)));
});
}
namespace crimson::osd {
+void OSDOperationRegistry::do_stop()
+{
+ // we need to decouple visiting the registry from destructing
+ // ops because of the auto-unlink feature of boost::intrusive.
+ // the list shouldn't change while iterating due to constrains
+ // on iterator's validity.
+ constexpr auto historic_reg_index =
+ static_cast<size_t>(OperationTypeCode::historic_client_request);
+ auto& historic_registry = get_registry<historic_reg_index>();
+ std::vector<ClientRequest::ICRef> to_ref_down;
+ std::transform(std::begin(historic_registry), std::end(historic_registry),
+ std::back_inserter(to_ref_down),
+ [] (const Operation& op) {
+ return ClientRequest::ICRef{
+ static_cast<const ClientRequest*>(&op),
+ /* add_ref= */ false
+ };
+ });
+ // to_ref_down is going off
+}
+
size_t OSDOperationRegistry::dump_client_requests(ceph::Formatter* f) const
{
const auto& client_registry =
return std::size(client_registry);
}
+size_t OSDOperationRegistry::dump_historic_client_requests(ceph::Formatter* f) const
+{
+ const auto& historic_client_registry =
+ get_registry<static_cast<size_t>(OperationTypeCode::historic_client_request)>(); //ClientRequest::type)>();
+ f->open_object_section("op_history");
+ f->dump_int("size", historic_client_registry.size());
+ // TODO: f->dump_int("duration", history_duration.load());
+ // the intrusive list is configured to not store the size
+ size_t ops_count = 0;
+ {
+ f->open_array_section("ops");
+ for (const auto& op : historic_client_registry) {
+ op.dump(f);
+ ++ops_count;
+ }
+ f->close_section();
+ }
+ f->close_section();
+ return ops_count;
+}
+
OperationThrottler::OperationThrottler(ConfigProxy &conf)
: scheduler(crimson::osd::scheduler::make_scheduler(conf))
{
background_recovery,
background_recovery_sub,
internal_client_request,
+ historic_client_request,
last_op
};
"background_recovery",
"background_recovery_sub",
"internal_client_request",
+ "historic_client_request",
};
// prevent the addition of OperationTypeCode-s with no matching OP_NAMES entry:
struct OperationT : InterruptibleOperation {
static constexpr const char *type_name = OP_NAMES[static_cast<int>(T::type)];
using IRef = boost::intrusive_ptr<T>;
+ using ICRef = boost::intrusive_ptr<const T>;
unsigned get_type() const final {
return static_cast<unsigned>(T::type);
struct OSDOperationRegistry : OperationRegistryT<
static_cast<size_t>(OperationTypeCode::last_op)
> {
+ void do_stop() override;
size_t dump_client_requests(ceph::Formatter* f) const;
+ size_t dump_historic_client_requests(ceph::Formatter* f) const;
+
};
/**
* Throttles set of currently running operations
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/config.h"
+#include "crimson/osd/osd.h"
+#include "crimson/osd/osd_operation_external_tracking.h"
+
+namespace {
+ seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_osd);
+ }
+}
+
+namespace crimson::osd {
+
+void HistoricBackend::handle(ClientRequest::CompletionEvent&,
+ const Operation& op)
+{
+ // early exit if the history is disabled
+ using crimson::common::local_conf;
+ if (!local_conf()->osd_op_history_size) {
+ return;
+ }
+
+#ifdef NDEBUG
+ const auto& client_request = static_cast<const ClientRequest&>(op);
+#else
+ const auto& client_request = dynamic_cast<const ClientRequest&>(op);
+#endif
+ auto& main_registry = client_request.osd.get_shard_services().registry;
+
+ // unlink the op from the client request registry. this is a part of
+ // the re-link procedure. finally it will be in historic registry.
+ constexpr auto client_reg_index =
+ static_cast<size_t>(OperationTypeCode::client_request);
+ constexpr auto historic_reg_index =
+ static_cast<size_t>(OperationTypeCode::historic_client_request);
+ auto& client_registry = main_registry.get_registry<client_reg_index>();
+ auto& historic_registry = main_registry.get_registry<historic_reg_index>();
+
+ historic_registry.splice(std::end(historic_registry),
+ client_registry,
+ client_registry.iterator_to(client_request));
+ ClientRequest::ICRef(
+ &client_request, /* add_ref= */true
+ ).detach(); // yes, "leak" it for now!
+
+ // check whether the history size limit is not exceeded; if so, then
+ // purge the oldest op.
+ // NOTE: Operation uses the auto-unlink feature of boost::intrusive.
+ // NOTE: the cleaning happens in OSDOperationRegistry::do_stop()
+ if (historic_registry.size() > local_conf()->osd_op_history_size) {
+ const auto& oldest_historic_op =
+ static_cast<const ClientRequest&>(historic_registry.front());
+ // clear a previously "leaked" op
+ ClientRequest::ICRef(&oldest_historic_op, /* add_ref= */false);
+ }
+}
+
+} // namespace crimson::osd
const Operation&) override {}
};
+struct HistoricBackend
+ : ClientRequest::StartEvent::Backend,
+ ConnectionPipeline::AwaitActive::BlockingEvent::Backend,
+ ConnectionPipeline::AwaitMap::BlockingEvent::Backend,
+ ConnectionPipeline::GetPG::BlockingEvent::Backend,
+ OSD_OSDMapGate::OSDMapBlocker::BlockingEvent::Backend,
+ PGMap::PGCreationBlockingEvent::Backend,
+ ClientRequest::PGPipeline::AwaitMap::BlockingEvent::Backend,
+ PG_OSDMapGate::OSDMapBlocker::BlockingEvent::Backend,
+ ClientRequest::PGPipeline::WaitForActive::BlockingEvent::Backend,
+ PGActivationBlocker::BlockingEvent::Backend,
+ ClientRequest::PGPipeline::RecoverMissing::BlockingEvent::Backend,
+ ClientRequest::PGPipeline::GetOBC::BlockingEvent::Backend,
+ ClientRequest::PGPipeline::Process::BlockingEvent::Backend,
+ ClientRequest::PGPipeline::WaitRepop::BlockingEvent::Backend,
+ ClientRequest::PGPipeline::WaitRepop::BlockingEvent::ExitBarrierEvent::Backend,
+ ClientRequest::PGPipeline::SendReply::BlockingEvent::Backend,
+ ClientRequest::CompletionEvent::Backend
+{
+ void handle(ClientRequest::StartEvent&,
+ const Operation&) override {}
+
+ void handle(ConnectionPipeline::AwaitActive::BlockingEvent& ev,
+ const Operation& op,
+ const ConnectionPipeline::AwaitActive& blocker) override {
+ }
+
+ void handle(ConnectionPipeline::AwaitMap::BlockingEvent& ev,
+ const Operation& op,
+ const ConnectionPipeline::AwaitMap& blocker) override {
+ }
+
+ void handle(OSD_OSDMapGate::OSDMapBlocker::BlockingEvent&,
+ const Operation&,
+ const OSD_OSDMapGate::OSDMapBlocker&) override {
+ }
+
+ void handle(ConnectionPipeline::GetPG::BlockingEvent& ev,
+ const Operation& op,
+ const ConnectionPipeline::GetPG& blocker) override {
+ }
+
+ void handle(PGMap::PGCreationBlockingEvent&,
+ const Operation&,
+ const PGMap::PGCreationBlocker&) override {
+ }
+
+ void handle(ClientRequest::PGPipeline::AwaitMap::BlockingEvent& ev,
+ const Operation& op,
+ const ClientRequest::PGPipeline::AwaitMap& blocker) override {
+ }
+
+ void handle(PG_OSDMapGate::OSDMapBlocker::BlockingEvent&,
+ const Operation&,
+ const PG_OSDMapGate::OSDMapBlocker&) override {
+ }
+
+ void handle(ClientRequest::PGPipeline::WaitForActive::BlockingEvent& ev,
+ const Operation& op,
+ const ClientRequest::PGPipeline::WaitForActive& blocker) override {
+ }
+
+ void handle(PGActivationBlocker::BlockingEvent& ev,
+ const Operation& op,
+ const PGActivationBlocker& blocker) override {
+ }
+
+ void handle(ClientRequest::PGPipeline::RecoverMissing::BlockingEvent& ev,
+ const Operation& op,
+ const ClientRequest::PGPipeline::RecoverMissing& blocker) override {
+ }
+
+ void handle(ClientRequest::PGPipeline::GetOBC::BlockingEvent& ev,
+ const Operation& op,
+ const ClientRequest::PGPipeline::GetOBC& blocker) override {
+ }
+
+ void handle(ClientRequest::PGPipeline::Process::BlockingEvent& ev,
+ const Operation& op,
+ const ClientRequest::PGPipeline::Process& blocker) override {
+ }
+
+ void handle(ClientRequest::PGPipeline::WaitRepop::BlockingEvent& ev,
+ const Operation& op,
+ const ClientRequest::PGPipeline::WaitRepop& blocker) override {
+ }
+
+ void handle(ClientRequest::PGPipeline::WaitRepop::BlockingEvent::ExitBarrierEvent& ev,
+ const Operation& op) override {
+ }
+
+ void handle(ClientRequest::PGPipeline::SendReply::BlockingEvent& ev,
+ const Operation& op,
+ const ClientRequest::PGPipeline::SendReply& blocker) override {
+ }
+
+ void handle(ClientRequest::CompletionEvent&,
+ const Operation&) override;
+};
} // namespace crimson::osd
template <>
struct EventBackendRegistry<osd::ClientRequest> {
- static std::tuple<osd::LttngBackend/*, HistoricBackend*/> get_backends() {
- return { {} };
+ static std::tuple<osd::LttngBackend, osd::HistoricBackend> get_backends() {
+ return { {}, {} };
}
};
} send_reply;
friend class ClientRequest;
friend class LttngBackend;
+ friend class HistoricBackend;
};
using ordering_hook_t = boost::intrusive::list_member_hook<>;