]>
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 | ||
f67539c2 TL |
4 | #ifndef CEPH_LIBRBD_CACHE_PWL_REQUEST_H |
5 | #define CEPH_LIBRBD_CACHE_PWL_REQUEST_H | |
9f95a23c TL |
6 | |
7 | #include "include/Context.h" | |
f67539c2 TL |
8 | #include "librbd/cache/pwl/Types.h" |
9 | #include "librbd/cache/pwl/LogOperation.h" | |
9f95a23c TL |
10 | |
11 | namespace librbd { | |
12 | class BlockGuardCell; | |
13 | ||
14 | namespace cache { | |
f67539c2 | 15 | namespace pwl { |
9f95a23c TL |
16 | |
17 | class GuardedRequestFunctionContext; | |
18 | ||
19 | struct WriteRequestResources { | |
20 | bool allocated = false; | |
21 | std::vector<WriteBufferAllocation> buffers; | |
22 | }; | |
23 | ||
24 | /** | |
25 | * A request that can be deferred in a BlockGuard to sequence | |
26 | * overlapping operations. | |
27 | * This is the custodian of the BlockGuard cell for this IO, and the | |
28 | * state information about the progress of this IO. This object lives | |
29 | * until the IO is persisted in all (live) log replicas. User request | |
30 | * may be completed from here before the IO persists. | |
31 | */ | |
32 | template <typename T> | |
33 | class C_BlockIORequest : public Context { | |
34 | public: | |
f67539c2 | 35 | T &pwl; |
9f95a23c TL |
36 | io::Extents image_extents; |
37 | bufferlist bl; | |
38 | int fadvise_flags; | |
39 | Context *user_req; /* User write request */ | |
40 | ExtentsSummary<io::Extents> image_extents_summary; | |
41 | bool detained = false; /* Detained in blockguard (overlapped with a prior IO) */ | |
42 | utime_t allocated_time; /* When allocation began */ | |
9f95a23c | 43 | |
f67539c2 | 44 | C_BlockIORequest(T &pwl, const utime_t arrived, io::Extents &&extents, |
9f95a23c TL |
45 | bufferlist&& bl, const int fadvise_flags, Context *user_req); |
46 | ~C_BlockIORequest() override; | |
47 | C_BlockIORequest(const C_BlockIORequest&) = delete; | |
48 | C_BlockIORequest &operator=(const C_BlockIORequest&) = delete; | |
49 | ||
50 | void set_cell(BlockGuardCell *cell); | |
51 | BlockGuardCell *get_cell(void); | |
52 | void release_cell(); | |
53 | ||
54 | void complete_user_request(int r); | |
55 | void finish(int r); | |
56 | virtual void finish_req(int r) = 0; | |
57 | ||
58 | virtual bool alloc_resources() = 0; | |
59 | ||
60 | void deferred(); | |
61 | ||
62 | virtual void deferred_handler() = 0; | |
63 | ||
64 | virtual void dispatch() = 0; | |
65 | ||
f67539c2 TL |
66 | virtual void copy_cache() {}; |
67 | ||
9f95a23c TL |
68 | virtual const char *get_name() const { |
69 | return "C_BlockIORequest"; | |
70 | } | |
20effc67 | 71 | |
9f95a23c TL |
72 | uint64_t get_image_extents_size() { |
73 | return image_extents.size(); | |
74 | } | |
20effc67 | 75 | |
9f95a23c TL |
76 | std::vector<WriteBufferAllocation>& get_resources_buffers() { |
77 | return m_resources.buffers; | |
78 | } | |
79 | ||
80 | void set_allocated(bool allocated) { | |
81 | if (allocated) { | |
82 | m_resources.allocated = true; | |
83 | } else { | |
84 | m_resources.buffers.clear(); | |
85 | } | |
86 | } | |
87 | ||
88 | virtual void setup_buffer_resources( | |
f67539c2 TL |
89 | uint64_t *bytes_cached, uint64_t *bytes_dirtied, uint64_t *bytes_allocated, |
90 | uint64_t *number_lanes, uint64_t *number_log_entries, | |
a4b75251 | 91 | uint64_t *number_unpublished_reserves) = 0; |
9f95a23c TL |
92 | |
93 | protected: | |
94 | utime_t m_arrived_time; | |
95 | utime_t m_dispatched_time; /* When dispatch began */ | |
96 | utime_t m_user_req_completed_time; | |
97 | std::atomic<bool> m_deferred = {false}; /* Deferred because this or a prior IO had to wait for write resources */ | |
98 | WriteRequestResources m_resources; | |
99 | ||
100 | private: | |
101 | std::atomic<bool> m_user_req_completed = {false}; | |
102 | std::atomic<bool> m_finish_called = {false}; | |
103 | std::atomic<bool> m_cell_released = {false}; | |
104 | BlockGuardCell* m_cell = nullptr; | |
105 | ||
106 | template <typename U> | |
107 | friend std::ostream &operator<<(std::ostream &os, | |
108 | const C_BlockIORequest<U> &req); | |
109 | }; | |
110 | ||
111 | /** | |
112 | * This is the custodian of the BlockGuard cell for this write. Block | |
113 | * guard is not released until the write persists everywhere (this is | |
114 | * how we guarantee to each log replica that they will never see | |
115 | * overlapping writes). | |
116 | */ | |
117 | template <typename T> | |
118 | class C_WriteRequest : public C_BlockIORequest<T> { | |
119 | public: | |
f67539c2 TL |
120 | using C_BlockIORequest<T>::pwl; |
121 | bool compare_succeeded = false; | |
122 | uint64_t *mismatch_offset; | |
123 | bufferlist cmp_bl; | |
124 | bufferlist read_bl; | |
125 | bool is_comp_and_write = false; | |
20effc67 | 126 | std::unique_ptr<WriteLogOperationSet> op_set = nullptr; |
9f95a23c | 127 | |
f67539c2 | 128 | C_WriteRequest(T &pwl, const utime_t arrived, io::Extents &&image_extents, |
9f95a23c TL |
129 | bufferlist&& bl, const int fadvise_flags, ceph::mutex &lock, |
130 | PerfCounters *perfcounter, Context *user_req); | |
131 | ||
f67539c2 TL |
132 | C_WriteRequest(T &pwl, const utime_t arrived, io::Extents &&image_extents, |
133 | bufferlist&& cmp_bl, bufferlist&& bl, uint64_t *mismatch_offset, | |
134 | int fadvise_flags, ceph::mutex &lock, PerfCounters *perfcounter, | |
135 | Context *user_req); | |
136 | ||
9f95a23c TL |
137 | ~C_WriteRequest() override; |
138 | ||
139 | void blockguard_acquired(GuardedRequestFunctionContext &guard_ctx); | |
140 | ||
141 | /* Common finish to plain write and compare-and-write (if it writes) */ | |
142 | void finish_req(int r) override; | |
143 | ||
144 | /* Compare and write will override this */ | |
f67539c2 TL |
145 | virtual void update_req_stats(utime_t &now); |
146 | ||
9f95a23c TL |
147 | bool alloc_resources() override; |
148 | ||
149 | void deferred_handler() override { } | |
150 | ||
151 | void dispatch() override; | |
152 | ||
f67539c2 TL |
153 | void copy_cache() override; |
154 | ||
155 | virtual std::shared_ptr<WriteLogOperation> create_operation(uint64_t offset, | |
156 | uint64_t len); | |
157 | ||
158 | virtual void setup_log_operations(DeferredContexts &on_exit); | |
9f95a23c TL |
159 | |
160 | bool append_write_request(std::shared_ptr<SyncPoint> sync_point); | |
161 | ||
162 | virtual void schedule_append(); | |
163 | ||
164 | const char *get_name() const override { | |
165 | return "C_WriteRequest"; | |
166 | } | |
167 | ||
168 | protected: | |
169 | using C_BlockIORequest<T>::m_resources; | |
f67539c2 | 170 | PerfCounters *m_perfcounter = nullptr; |
9f95a23c TL |
171 | |
172 | private: | |
173 | bool m_do_early_flush = false; | |
174 | std::atomic<int> m_appended = {0}; | |
175 | bool m_queued = false; | |
176 | ceph::mutex &m_lock; | |
9f95a23c TL |
177 | template <typename U> |
178 | friend std::ostream &operator<<(std::ostream &os, | |
179 | const C_WriteRequest<U> &req); | |
180 | }; | |
181 | ||
f67539c2 TL |
182 | /** |
183 | * This is the custodian of the BlockGuard cell for this | |
184 | * aio_flush. Block guard is released as soon as the new | |
185 | * sync point (if required) is created. Subsequent IOs can | |
186 | * proceed while this flush waits for prior IOs to complete | |
187 | * and any required sync points to be persisted. | |
188 | */ | |
189 | template <typename T> | |
190 | class C_FlushRequest : public C_BlockIORequest<T> { | |
191 | public: | |
192 | using C_BlockIORequest<T>::pwl; | |
193 | bool internal = false; | |
194 | std::shared_ptr<SyncPoint> to_append; | |
195 | ||
196 | C_FlushRequest(T &pwl, const utime_t arrived, | |
197 | io::Extents &&image_extents, | |
198 | bufferlist&& bl, const int fadvise_flags, | |
199 | ceph::mutex &lock, PerfCounters *perfcounter, | |
200 | Context *user_req); | |
201 | ||
202 | ~C_FlushRequest() override {} | |
203 | ||
204 | bool alloc_resources() override; | |
205 | ||
206 | void dispatch() override; | |
207 | ||
208 | const char *get_name() const override { | |
209 | return "C_FlushRequest"; | |
210 | } | |
211 | ||
212 | void setup_buffer_resources( | |
213 | uint64_t *bytes_cached, uint64_t *bytes_dirtied, | |
214 | uint64_t *bytes_allocated, uint64_t *number_lanes, | |
215 | uint64_t *number_log_entries, | |
216 | uint64_t *number_unpublished_reserves) override; | |
217 | private: | |
218 | std::shared_ptr<SyncPointLogOperation> op; | |
219 | ceph::mutex &m_lock; | |
220 | PerfCounters *m_perfcounter = nullptr; | |
221 | ||
222 | void finish_req(int r) override; | |
223 | void deferred_handler() override { | |
224 | m_perfcounter->inc(l_librbd_pwl_aio_flush_def, 1); | |
225 | } | |
226 | ||
227 | template <typename U> | |
228 | friend std::ostream &operator<<(std::ostream &os, | |
229 | const C_FlushRequest<U> &req); | |
230 | }; | |
231 | ||
232 | /** | |
233 | * This is the custodian of the BlockGuard cell for this discard. As in the | |
234 | * case of write, the block guard is not released until the discard persists | |
235 | * everywhere. | |
236 | */ | |
237 | template <typename T> | |
238 | class C_DiscardRequest : public C_BlockIORequest<T> { | |
239 | public: | |
240 | using C_BlockIORequest<T>::pwl; | |
241 | std::shared_ptr<DiscardLogOperation> op; | |
242 | ||
243 | C_DiscardRequest(T &pwl, const utime_t arrived, io::Extents &&image_extents, | |
244 | uint32_t discard_granularity_bytes, ceph::mutex &lock, | |
245 | PerfCounters *perfcounter, Context *user_req); | |
246 | ||
247 | ~C_DiscardRequest() override; | |
248 | void finish_req(int r) override {} | |
249 | ||
250 | bool alloc_resources() override; | |
251 | ||
252 | void deferred_handler() override { } | |
253 | ||
254 | void setup_log_operations(); | |
255 | ||
256 | void dispatch() override; | |
257 | ||
258 | void blockguard_acquired(GuardedRequestFunctionContext &guard_ctx); | |
259 | ||
260 | const char *get_name() const override { | |
261 | return "C_DiscardRequest"; | |
262 | } | |
263 | void setup_buffer_resources( | |
264 | uint64_t *bytes_cached, uint64_t *bytes_dirtied, uint64_t *bytes_allocated, | |
265 | uint64_t *number_lanes, uint64_t *number_log_entries, | |
266 | uint64_t *number_unpublished_reserves) override; | |
267 | private: | |
268 | uint32_t m_discard_granularity_bytes; | |
269 | ceph::mutex &m_lock; | |
270 | PerfCounters *m_perfcounter = nullptr; | |
271 | template <typename U> | |
272 | friend std::ostream &operator<<(std::ostream &os, | |
273 | const C_DiscardRequest<U> &req); | |
274 | }; | |
275 | ||
276 | /** | |
277 | * This is the custodian of the BlockGuard cell for this write same. | |
278 | * | |
279 | * A writesame allocates and persists a data buffer like a write, but the | |
280 | * data buffer is usually much shorter than the write same. | |
281 | */ | |
282 | template <typename T> | |
283 | class C_WriteSameRequest : public C_WriteRequest<T> { | |
284 | public: | |
285 | using C_BlockIORequest<T>::pwl; | |
286 | C_WriteSameRequest(T &pwl, const utime_t arrived, io::Extents &&image_extents, | |
287 | bufferlist&& bl, const int fadvise_flags, ceph::mutex &lock, | |
288 | PerfCounters *perfcounter, Context *user_req); | |
289 | ||
290 | ~C_WriteSameRequest() override; | |
291 | ||
292 | void update_req_stats(utime_t &now) override; | |
293 | ||
294 | std::shared_ptr<WriteLogOperation> create_operation(uint64_t offset, uint64_t len) override; | |
295 | ||
296 | const char *get_name() const override { | |
297 | return "C_WriteSameRequest"; | |
298 | } | |
299 | ||
300 | template<typename U> | |
301 | friend std::ostream &operator<<(std::ostream &os, | |
302 | const C_WriteSameRequest<U> &req); | |
303 | }; | |
304 | ||
9f95a23c TL |
305 | struct BlockGuardReqState { |
306 | bool barrier = false; /* This is a barrier request */ | |
307 | bool current_barrier = false; /* This is the currently active barrier */ | |
308 | bool detained = false; | |
309 | bool queued = false; /* Queued for barrier */ | |
310 | friend std::ostream &operator<<(std::ostream &os, | |
f67539c2 | 311 | const BlockGuardReqState &r) { |
20effc67 TL |
312 | os << "barrier=" << r.barrier |
313 | << ", current_barrier=" << r.current_barrier | |
314 | << ", detained=" << r.detained | |
315 | << ", queued=" << r.queued; | |
f67539c2 TL |
316 | return os; |
317 | } | |
9f95a23c TL |
318 | }; |
319 | ||
320 | class GuardedRequestFunctionContext : public Context { | |
321 | public: | |
322 | BlockGuardCell *cell = nullptr; | |
323 | BlockGuardReqState state; | |
f67539c2 TL |
324 | GuardedRequestFunctionContext(boost::function<void(GuardedRequestFunctionContext&)> &&callback) |
325 | : m_callback(std::move(callback)){ } | |
326 | ~GuardedRequestFunctionContext(void) override { }; | |
9f95a23c TL |
327 | GuardedRequestFunctionContext(const GuardedRequestFunctionContext&) = delete; |
328 | GuardedRequestFunctionContext &operator=(const GuardedRequestFunctionContext&) = delete; | |
329 | ||
330 | private: | |
331 | boost::function<void(GuardedRequestFunctionContext&)> m_callback; | |
f67539c2 TL |
332 | void finish(int r) override { |
333 | ceph_assert(cell); | |
334 | m_callback(*this); | |
335 | } | |
9f95a23c TL |
336 | }; |
337 | ||
338 | class GuardedRequest { | |
339 | public: | |
340 | const BlockExtent block_extent; | |
341 | GuardedRequestFunctionContext *guard_ctx; /* Work to do when guard on range obtained */ | |
342 | ||
343 | GuardedRequest(const BlockExtent block_extent, | |
344 | GuardedRequestFunctionContext *on_guard_acquire, bool barrier = false) | |
345 | : block_extent(block_extent), guard_ctx(on_guard_acquire) { | |
346 | guard_ctx->state.barrier = barrier; | |
347 | } | |
348 | friend std::ostream &operator<<(std::ostream &os, | |
f67539c2 | 349 | const GuardedRequest &r) { |
20effc67 TL |
350 | os << "guard_ctx->state=[" << r.guard_ctx->state |
351 | << "], block_extent.block_start=" << r.block_extent.block_start | |
352 | << ", block_extent.block_end=" << r.block_extent.block_end; | |
f67539c2 TL |
353 | return os; |
354 | } | |
9f95a23c TL |
355 | }; |
356 | ||
f67539c2 | 357 | } // namespace pwl |
9f95a23c TL |
358 | } // namespace cache |
359 | } // namespace librbd | |
360 | ||
f67539c2 | 361 | #endif // CEPH_LIBRBD_CACHE_PWL_REQUEST_H |