]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #pragma once | |
5 | ||
20effc67 TL |
6 | #include "crimson/common/operation.h" |
7 | #include "crimson/osd/pg_interval_interrupt_condition.h" | |
f67539c2 | 8 | #include "crimson/osd/scheduler/scheduler.h" |
20effc67 | 9 | #include "osd/osd_types.h" |
9f95a23c | 10 | |
1e59de90 TL |
11 | namespace crimson::os::seastore { |
12 | template<class OpT> | |
13 | class OperationProxyT; | |
14 | } | |
15 | ||
9f95a23c TL |
16 | namespace crimson::osd { |
17 | ||
1e59de90 TL |
18 | /// Ordering stages for a class of operations ordered by PG. |
19 | struct ConnectionPipeline { | |
20 | struct AwaitActive : OrderedExclusivePhaseT<AwaitActive> { | |
21 | static constexpr auto type_name = | |
22 | "ConnectionPipeline::await_active"; | |
23 | } await_active; | |
24 | ||
25 | struct AwaitMap : OrderedExclusivePhaseT<AwaitMap> { | |
26 | static constexpr auto type_name = | |
27 | "ConnectionPipeline::await_map"; | |
28 | } await_map; | |
29 | ||
30 | struct GetPG : OrderedExclusivePhaseT<GetPG> { | |
31 | static constexpr auto type_name = | |
32 | "ConnectionPipeline::get_pg"; | |
33 | } get_pg; | |
34 | }; | |
35 | ||
9f95a23c TL |
36 | enum class OperationTypeCode { |
37 | client_request = 0, | |
f67539c2 | 38 | peering_event, |
f67539c2 TL |
39 | pg_advance_map, |
40 | pg_creation, | |
41 | replicated_request, | |
42 | background_recovery, | |
43 | background_recovery_sub, | |
20effc67 | 44 | internal_client_request, |
1e59de90 TL |
45 | historic_client_request, |
46 | logmissing_request, | |
47 | logmissing_request_reply, | |
48 | snaptrim_event, | |
49 | snaptrimobj_subevent, | |
f67539c2 | 50 | last_op |
9f95a23c TL |
51 | }; |
52 | ||
53 | static constexpr const char* const OP_NAMES[] = { | |
54 | "client_request", | |
55 | "peering_event", | |
9f95a23c TL |
56 | "pg_advance_map", |
57 | "pg_creation", | |
58 | "replicated_request", | |
f67539c2 TL |
59 | "background_recovery", |
60 | "background_recovery_sub", | |
20effc67 | 61 | "internal_client_request", |
1e59de90 TL |
62 | "historic_client_request", |
63 | "logmissing_request", | |
64 | "logmissing_request_reply", | |
65 | "snaptrim_event", | |
66 | "snaptrimobj_subevent", | |
9f95a23c TL |
67 | }; |
68 | ||
69 | // prevent the addition of OperationTypeCode-s with no matching OP_NAMES entry: | |
70 | static_assert( | |
71 | (sizeof(OP_NAMES)/sizeof(OP_NAMES[0])) == | |
72 | static_cast<int>(OperationTypeCode::last_op)); | |
73 | ||
20effc67 TL |
74 | struct InterruptibleOperation : Operation { |
75 | template <typename ValuesT = void> | |
76 | using interruptible_future = | |
77 | ::crimson::interruptible::interruptible_future< | |
78 | ::crimson::osd::IOInterruptCondition, ValuesT>; | |
79 | using interruptor = | |
80 | ::crimson::interruptible::interruptor< | |
81 | ::crimson::osd::IOInterruptCondition>; | |
9f95a23c | 82 | }; |
9f95a23c TL |
83 | |
84 | template <typename T> | |
1e59de90 | 85 | struct OperationT : InterruptibleOperation { |
9f95a23c TL |
86 | static constexpr const char *type_name = OP_NAMES[static_cast<int>(T::type)]; |
87 | using IRef = boost::intrusive_ptr<T>; | |
1e59de90 | 88 | using ICRef = boost::intrusive_ptr<const T>; |
9f95a23c | 89 | |
20effc67 TL |
90 | unsigned get_type() const final { |
91 | return static_cast<unsigned>(T::type); | |
9f95a23c TL |
92 | } |
93 | ||
94 | const char *get_type_name() const final { | |
95 | return T::type_name; | |
96 | } | |
97 | ||
98 | virtual ~OperationT() = default; | |
f67539c2 TL |
99 | |
100 | private: | |
101 | virtual void dump_detail(ceph::Formatter *f) const = 0; | |
9f95a23c TL |
102 | }; |
103 | ||
1e59de90 TL |
104 | template <class T> |
105 | class TrackableOperationT : public OperationT<T> { | |
106 | T* that() { | |
107 | return static_cast<T*>(this); | |
108 | } | |
109 | const T* that() const { | |
110 | return static_cast<const T*>(this); | |
111 | } | |
112 | ||
113 | protected: | |
114 | template<class EventT> | |
115 | decltype(auto) get_event() { | |
116 | // all out derivates are supposed to define the list of tracking | |
117 | // events accessible via `std::get`. This will usually boil down | |
118 | // into an instance of `std::tuple`. | |
119 | return std::get<EventT>(that()->tracking_events); | |
120 | } | |
121 | ||
122 | template<class EventT> | |
123 | decltype(auto) get_event() const { | |
124 | return std::get<EventT>(that()->tracking_events); | |
125 | } | |
126 | ||
127 | using OperationT<T>::OperationT; | |
128 | ||
129 | struct StartEvent : TimeEvent<StartEvent> {}; | |
130 | struct CompletionEvent : TimeEvent<CompletionEvent> {}; | |
131 | ||
132 | template <class EventT, class... Args> | |
133 | void track_event(Args&&... args) { | |
134 | // the idea is to have a visitor-like interface that allows to double | |
135 | // dispatch (backend, blocker type) | |
136 | get_event<EventT>().trigger(*that(), std::forward<Args>(args)...); | |
137 | } | |
138 | ||
139 | template <class BlockingEventT, class InterruptorT=void, class F> | |
140 | auto with_blocking_event(F&& f) { | |
141 | auto ret = std::forward<F>(f)(typename BlockingEventT::template Trigger<T>{ | |
142 | get_event<BlockingEventT>(), *that() | |
143 | }); | |
144 | if constexpr (std::is_same_v<InterruptorT, void>) { | |
145 | return ret; | |
146 | } else { | |
147 | using ret_t = decltype(ret); | |
148 | return typename InterruptorT::template futurize_t<ret_t>{std::move(ret)}; | |
149 | } | |
150 | } | |
151 | ||
152 | public: | |
153 | static constexpr bool is_trackable = true; | |
154 | }; | |
155 | ||
156 | template <class T> | |
157 | class PhasedOperationT : public TrackableOperationT<T> { | |
158 | using base_t = TrackableOperationT<T>; | |
159 | ||
160 | T* that() { | |
161 | return static_cast<T*>(this); | |
162 | } | |
163 | const T* that() const { | |
164 | return static_cast<const T*>(this); | |
165 | } | |
166 | ||
167 | protected: | |
168 | using TrackableOperationT<T>::TrackableOperationT; | |
169 | ||
170 | template <class InterruptorT=void, class StageT> | |
171 | auto enter_stage(StageT& stage) { | |
172 | return this->template with_blocking_event<typename StageT::BlockingEvent, | |
173 | InterruptorT>( | |
174 | [&stage, this] (auto&& trigger) { | |
175 | // delegated storing the pipeline handle to let childs to match | |
176 | // the lifetime of pipeline with e.g. ConnectedSocket (important | |
177 | // for ConnectionPipeline). | |
178 | return that()->get_handle().template enter<T>(stage, std::move(trigger)); | |
179 | }); | |
180 | } | |
181 | ||
182 | template <class OpT> | |
183 | friend class crimson::os::seastore::OperationProxyT; | |
184 | ||
185 | // PGShardManager::start_pg_operation needs access to enter_stage, we can make this | |
186 | // more sophisticated later on | |
187 | friend class PGShardManager; | |
188 | }; | |
189 | ||
9f95a23c TL |
190 | /** |
191 | * Maintains a set of lists of all active ops. | |
192 | */ | |
1e59de90 | 193 | struct OSDOperationRegistry : OperationRegistryT< |
20effc67 | 194 | static_cast<size_t>(OperationTypeCode::last_op) |
1e59de90 TL |
195 | > { |
196 | OSDOperationRegistry(); | |
f67539c2 | 197 | |
1e59de90 TL |
198 | void do_stop() override; |
199 | ||
200 | void put_historic(const class ClientRequest& op); | |
201 | ||
202 | size_t dump_historic_client_requests(ceph::Formatter* f) const; | |
203 | size_t dump_slowest_historic_client_requests(ceph::Formatter* f) const; | |
204 | ||
205 | private: | |
206 | op_list::const_iterator last_of_recents; | |
207 | size_t num_recent_ops = 0; | |
208 | size_t num_slow_ops = 0; | |
209 | }; | |
f67539c2 TL |
210 | /** |
211 | * Throttles set of currently running operations | |
212 | * | |
213 | * Very primitive currently, assumes all ops are equally | |
214 | * expensive and simply limits the number that can be | |
215 | * concurrently active. | |
216 | */ | |
1e59de90 | 217 | class OperationThrottler : public BlockerT<OperationThrottler>, |
f67539c2 | 218 | private md_config_obs_t { |
1e59de90 TL |
219 | friend BlockerT<OperationThrottler>; |
220 | static constexpr const char* type_name = "OperationThrottler"; | |
f67539c2 | 221 | |
1e59de90 | 222 | template <typename OperationT, typename F> |
f67539c2 | 223 | auto with_throttle( |
1e59de90 | 224 | OperationT* op, |
f67539c2 TL |
225 | crimson::osd::scheduler::params_t params, |
226 | F &&f) { | |
227 | if (!max_in_progress) return f(); | |
1e59de90 | 228 | return acquire_throttle(params) |
f67539c2 TL |
229 | .then(std::forward<F>(f)) |
230 | .then([this](auto x) { | |
231 | release_throttle(); | |
232 | return x; | |
233 | }); | |
234 | } | |
235 | ||
1e59de90 | 236 | template <typename OperationT, typename F> |
f67539c2 | 237 | seastar::future<> with_throttle_while( |
1e59de90 | 238 | OperationT* op, |
f67539c2 TL |
239 | crimson::osd::scheduler::params_t params, |
240 | F &&f) { | |
241 | return with_throttle(op, params, f).then([this, params, op, f](bool cont) { | |
1e59de90 | 242 | return cont ? with_throttle_while(op, params, f) : seastar::now(); |
f67539c2 TL |
243 | }); |
244 | } | |
245 | ||
1e59de90 TL |
246 | |
247 | public: | |
248 | OperationThrottler(ConfigProxy &conf); | |
249 | ||
250 | const char** get_tracked_conf_keys() const final; | |
251 | void handle_conf_change(const ConfigProxy& conf, | |
252 | const std::set<std::string> &changed) final; | |
253 | void update_from_config(const ConfigProxy &conf); | |
254 | ||
255 | template <class OpT, class... Args> | |
256 | seastar::future<> with_throttle_while( | |
257 | BlockingEvent::Trigger<OpT>&& trigger, | |
258 | Args&&... args) { | |
259 | return trigger.maybe_record_blocking( | |
260 | with_throttle_while(std::forward<Args>(args)...), *this); | |
f67539c2 TL |
261 | } |
262 | ||
263 | private: | |
1e59de90 TL |
264 | void dump_detail(Formatter *f) const final; |
265 | ||
f67539c2 TL |
266 | crimson::osd::scheduler::SchedulerRef scheduler; |
267 | ||
268 | uint64_t max_in_progress = 0; | |
269 | uint64_t in_progress = 0; | |
270 | ||
271 | uint64_t pending = 0; | |
272 | ||
273 | void wake(); | |
274 | ||
1e59de90 | 275 | seastar::future<> acquire_throttle( |
f67539c2 TL |
276 | crimson::osd::scheduler::params_t params); |
277 | ||
278 | void release_throttle(); | |
9f95a23c TL |
279 | }; |
280 | ||
9f95a23c | 281 | } |