]> git.proxmox.com Git - ceph.git/blame - ceph/src/librbd/io/QosImageDispatch.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / librbd / io / QosImageDispatch.cc
CommitLineData
f67539c2
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#include "librbd/io/QosImageDispatch.h"
5#include "common/dout.h"
6#include "librbd/AsioEngine.h"
7#include "librbd/ImageCtx.h"
8#include "librbd/io/FlushTracker.h"
20effc67 9#include <utility>
f67539c2
TL
10
11#define dout_subsys ceph_subsys_rbd
12#undef dout_prefix
13#define dout_prefix *_dout << "librbd::io::QosImageDispatch: " << this << " " \
14 << __func__ << ": "
15
16namespace librbd {
17namespace io {
18
19namespace {
20
21uint64_t get_extent_length(const Extents& extents) {
22 uint64_t length = 0;
23 for (auto& extent : extents) {
24 length += extent.second;
25 }
26 return length;
27}
28
29uint64_t calculate_tokens(bool read_op, uint64_t extent_length, uint64_t flag) {
30 if (read_op && ((flag & IMAGE_DISPATCH_FLAG_QOS_WRITE_MASK) != 0)) {
31 return 0;
32 } else if (!read_op && ((flag & IMAGE_DISPATCH_FLAG_QOS_READ_MASK) != 0)) {
33 return 0;
34 }
35
36 return (((flag & IMAGE_DISPATCH_FLAG_QOS_BPS_MASK) != 0) ? extent_length : 1);
37}
38
20effc67 39static const std::pair<uint64_t, const char*> throttle_flags[] = {
f67539c2
TL
40 {IMAGE_DISPATCH_FLAG_QOS_IOPS_THROTTLE, "rbd_qos_iops_throttle" },
41 {IMAGE_DISPATCH_FLAG_QOS_BPS_THROTTLE, "rbd_qos_bps_throttle" },
42 {IMAGE_DISPATCH_FLAG_QOS_READ_IOPS_THROTTLE, "rbd_qos_read_iops_throttle" },
43 {IMAGE_DISPATCH_FLAG_QOS_WRITE_IOPS_THROTTLE, "rbd_qos_write_iops_throttle" },
44 {IMAGE_DISPATCH_FLAG_QOS_READ_BPS_THROTTLE, "rbd_qos_read_bps_throttle" },
45 {IMAGE_DISPATCH_FLAG_QOS_WRITE_BPS_THROTTLE, "rbd_qos_write_bps_throttle" }
46};
47
48} // anonymous namespace
49
50template <typename I>
51QosImageDispatch<I>::QosImageDispatch(I* image_ctx)
52 : m_image_ctx(image_ctx), m_flush_tracker(new FlushTracker<I>(image_ctx)) {
53 auto cct = m_image_ctx->cct;
54 ldout(cct, 5) << "ictx=" << image_ctx << dendl;
55
56 SafeTimer *timer;
57 ceph::mutex *timer_lock;
58 ImageCtx::get_timer_instance(cct, &timer, &timer_lock);
20effc67
TL
59 for (auto [flag, name] : throttle_flags) {
60 m_throttles.emplace_back(
61 flag,
62 new TokenBucketThrottle(cct, name, 0, 0, timer, timer_lock));
f67539c2
TL
63 }
64}
65
66template <typename I>
67QosImageDispatch<I>::~QosImageDispatch() {
68 for (auto t : m_throttles) {
69 delete t.second;
70 }
f67539c2
TL
71}
72
73template <typename I>
74void QosImageDispatch<I>::shut_down(Context* on_finish) {
75 m_flush_tracker->shut_down();
76 on_finish->complete(0);
77}
78
79template <typename I>
80void QosImageDispatch<I>::apply_qos_schedule_tick_min(uint64_t tick) {
81 for (auto pair : m_throttles) {
82 pair.second->set_schedule_tick_min(tick);
83 }
84}
85
86template <typename I>
87void QosImageDispatch<I>::apply_qos_limit(uint64_t flag, uint64_t limit,
88 uint64_t burst, uint64_t burst_seconds) {
89 auto cct = m_image_ctx->cct;
90 TokenBucketThrottle *throttle = nullptr;
91 for (auto pair : m_throttles) {
92 if (flag == pair.first) {
93 throttle = pair.second;
94 break;
95 }
96 }
97 ceph_assert(throttle != nullptr);
98
99 int r = throttle->set_limit(limit, burst, burst_seconds);
100 if (r < 0) {
101 lderr(cct) << throttle->get_name() << ": invalid qos parameter: "
102 << "burst(" << burst << ") is less than "
103 << "limit(" << limit << ")" << dendl;
104 // if apply failed, we should at least make sure the limit works.
105 throttle->set_limit(limit, 0, 1);
106 }
107
108 if (limit) {
109 m_qos_enabled_flag |= flag;
110 } else {
111 m_qos_enabled_flag &= ~flag;
112 }
113}
114
20effc67
TL
115template <typename I>
116void QosImageDispatch<I>::apply_qos_exclude_ops(uint64_t exclude_ops) {
117 m_qos_exclude_ops = exclude_ops;
118}
119
f67539c2
TL
120template <typename I>
121bool QosImageDispatch<I>::read(
122 AioCompletion* aio_comp, Extents &&image_extents, ReadResult &&read_result,
123 IOContext io_context, int op_flags, int read_flags,
124 const ZTracer::Trace &parent_trace, uint64_t tid,
125 std::atomic<uint32_t>* image_dispatch_flags,
126 DispatchResult* dispatch_result, Context** on_finish,
127 Context* on_dispatched) {
128 auto cct = m_image_ctx->cct;
129 ldout(cct, 20) << "tid=" << tid << ", image_extents=" << image_extents
130 << dendl;
131
20effc67
TL
132 if (m_qos_exclude_ops & RBD_IO_OPERATION_READ) {
133 return false;
134 }
135
f67539c2
TL
136 if (needs_throttle(true, image_extents, tid, image_dispatch_flags,
137 dispatch_result, on_finish, on_dispatched)) {
138 return true;
139 }
140
141 return false;
142}
143
144template <typename I>
145bool QosImageDispatch<I>::write(
146 AioCompletion* aio_comp, Extents &&image_extents, bufferlist &&bl,
1e59de90 147 int op_flags, const ZTracer::Trace &parent_trace,
f67539c2
TL
148 uint64_t tid, std::atomic<uint32_t>* image_dispatch_flags,
149 DispatchResult* dispatch_result, Context** on_finish,
150 Context* on_dispatched) {
151 auto cct = m_image_ctx->cct;
152 ldout(cct, 20) << "tid=" << tid << ", image_extents=" << image_extents
153 << dendl;
154
20effc67
TL
155 if (m_qos_exclude_ops & RBD_IO_OPERATION_WRITE) {
156 return false;
157 }
158
f67539c2
TL
159 if (needs_throttle(false, image_extents, tid, image_dispatch_flags,
160 dispatch_result, on_finish, on_dispatched)) {
161 return true;
162 }
163
164 return false;
165}
166
167template <typename I>
168bool QosImageDispatch<I>::discard(
169 AioCompletion* aio_comp, Extents &&image_extents,
1e59de90
TL
170 uint32_t discard_granularity_bytes, const ZTracer::Trace &parent_trace,
171 uint64_t tid, std::atomic<uint32_t>* image_dispatch_flags,
f67539c2
TL
172 DispatchResult* dispatch_result, Context** on_finish,
173 Context* on_dispatched) {
174 auto cct = m_image_ctx->cct;
175 ldout(cct, 20) << "tid=" << tid << ", image_extents=" << image_extents
176 << dendl;
177
20effc67
TL
178 if (m_qos_exclude_ops & RBD_IO_OPERATION_DISCARD) {
179 return false;
180 }
181
f67539c2
TL
182 if (needs_throttle(false, image_extents, tid, image_dispatch_flags,
183 dispatch_result, on_finish, on_dispatched)) {
184 return true;
185 }
186
187 return false;
188}
189
190template <typename I>
191bool QosImageDispatch<I>::write_same(
192 AioCompletion* aio_comp, Extents &&image_extents, bufferlist &&bl,
1e59de90 193 int op_flags, const ZTracer::Trace &parent_trace,
f67539c2
TL
194 uint64_t tid, std::atomic<uint32_t>* image_dispatch_flags,
195 DispatchResult* dispatch_result, Context** on_finish,
196 Context* on_dispatched) {
197 auto cct = m_image_ctx->cct;
198 ldout(cct, 20) << "tid=" << tid << ", image_extents=" << image_extents
199 << dendl;
200
20effc67
TL
201 if (m_qos_exclude_ops & RBD_IO_OPERATION_WRITE_SAME) {
202 return false;
203 }
204
f67539c2
TL
205 if (needs_throttle(false, image_extents, tid, image_dispatch_flags,
206 dispatch_result, on_finish, on_dispatched)) {
207 return true;
208 }
209
210 return false;
211}
212
213template <typename I>
214bool QosImageDispatch<I>::compare_and_write(
1e59de90
TL
215 AioCompletion* aio_comp, Extents &&image_extents,
216 bufferlist &&cmp_bl, bufferlist &&bl, uint64_t *mismatch_offset,
217 int op_flags, const ZTracer::Trace &parent_trace,
218 uint64_t tid, std::atomic<uint32_t>* image_dispatch_flags,
f67539c2
TL
219 DispatchResult* dispatch_result, Context** on_finish,
220 Context* on_dispatched) {
221 auto cct = m_image_ctx->cct;
222 ldout(cct, 20) << "tid=" << tid << ", image_extents=" << image_extents
223 << dendl;
224
20effc67
TL
225 if (m_qos_exclude_ops & RBD_IO_OPERATION_COMPARE_AND_WRITE) {
226 return false;
227 }
228
f67539c2
TL
229 if (needs_throttle(false, image_extents, tid, image_dispatch_flags,
230 dispatch_result, on_finish, on_dispatched)) {
231 return true;
232 }
233
234 return false;
235}
236
237template <typename I>
238bool QosImageDispatch<I>::flush(
239 AioCompletion* aio_comp, FlushSource flush_source,
240 const ZTracer::Trace &parent_trace, uint64_t tid,
241 std::atomic<uint32_t>* image_dispatch_flags,
242 DispatchResult* dispatch_result, Context** on_finish,
243 Context* on_dispatched) {
244 auto cct = m_image_ctx->cct;
245 ldout(cct, 20) << "tid=" << tid << dendl;
246
247 *dispatch_result = DISPATCH_RESULT_CONTINUE;
248 m_flush_tracker->flush(on_dispatched);
249 return true;
250}
251
252template <typename I>
253void QosImageDispatch<I>::handle_finished(int r, uint64_t tid) {
254 auto cct = m_image_ctx->cct;
255 ldout(cct, 20) << "tid=" << tid << dendl;
256
257 m_flush_tracker->finish_io(tid);
258}
259
260template <typename I>
261bool QosImageDispatch<I>::set_throttle_flag(
262 std::atomic<uint32_t>* image_dispatch_flags, uint32_t flag) {
263 uint32_t expected = image_dispatch_flags->load();
264 uint32_t desired;
265 do {
266 desired = expected | flag;
267 } while (!image_dispatch_flags->compare_exchange_weak(expected, desired));
268
269 return ((desired & IMAGE_DISPATCH_FLAG_QOS_MASK) ==
270 IMAGE_DISPATCH_FLAG_QOS_MASK);
271}
272
273template <typename I>
274bool QosImageDispatch<I>::needs_throttle(
275 bool read_op, const Extents& image_extents, uint64_t tid,
276 std::atomic<uint32_t>* image_dispatch_flags,
277 DispatchResult* dispatch_result, Context** on_finish,
278 Context* on_dispatched) {
279 auto cct = m_image_ctx->cct;
280 auto extent_length = get_extent_length(image_extents);
281 bool all_qos_flags_set = false;
282
283 if (!read_op) {
284 m_flush_tracker->start_io(tid);
285 *on_finish = new LambdaContext([this, tid, on_finish=*on_finish](int r) {
286 handle_finished(r, tid);
287 on_finish->complete(r);
288 });
289 }
290 *dispatch_result = DISPATCH_RESULT_CONTINUE;
291
292 auto qos_enabled_flag = m_qos_enabled_flag;
293 for (auto [flag, throttle] : m_throttles) {
294 if ((qos_enabled_flag & flag) == 0) {
295 all_qos_flags_set = set_throttle_flag(image_dispatch_flags, flag);
296 continue;
297 }
298
299 auto tokens = calculate_tokens(read_op, extent_length, flag);
300 if (tokens > 0 &&
301 throttle->get(tokens, this, &QosImageDispatch<I>::handle_throttle_ready,
302 Tag{image_dispatch_flags, on_dispatched}, flag)) {
303 ldout(cct, 15) << "on_dispatched=" << on_dispatched << ", "
304 << "flag=" << flag << dendl;
305 all_qos_flags_set = false;
306 } else {
307 all_qos_flags_set = set_throttle_flag(image_dispatch_flags, flag);
308 }
309 }
310 return !all_qos_flags_set;
311}
312
313template <typename I>
314void QosImageDispatch<I>::handle_throttle_ready(Tag&& tag, uint64_t flag) {
315 auto cct = m_image_ctx->cct;
316 ldout(cct, 15) << "on_dispatched=" << tag.on_dispatched << ", "
317 << "flag=" << flag << dendl;
318
319 if (set_throttle_flag(tag.image_dispatch_flags, flag)) {
320 // timer_lock is held -- so dispatch from outside the timer thread
321 m_image_ctx->asio_engine->post(tag.on_dispatched, 0);
322 }
323}
324
325} // namespace io
326} // namespace librbd
327
328template class librbd::io::QosImageDispatch<librbd::ImageCtx>;