]>
Commit | Line | Data |
---|---|---|
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/WriteBlockImageDispatch.h" | |
5 | #include "common/dout.h" | |
6 | #include "common/Cond.h" | |
7 | #include "librbd/ImageCtx.h" | |
8 | #include "librbd/Utils.h" | |
9 | #include "librbd/asio/ContextWQ.h" | |
10 | #include "librbd/io/AioCompletion.h" | |
11 | #include "librbd/io/ImageDispatchSpec.h" | |
12 | ||
13 | #define dout_subsys ceph_subsys_rbd | |
14 | #undef dout_prefix | |
15 | #define dout_prefix *_dout << "librbd::io::WriteBlockImageDispatch: " << this \ | |
16 | << " " << __func__ << ": " | |
17 | ||
18 | namespace librbd { | |
19 | namespace io { | |
20 | ||
21 | template <typename I> | |
22 | struct WriteBlockImageDispatch<I>::C_BlockedWrites : public Context { | |
23 | WriteBlockImageDispatch *dispatch; | |
24 | explicit C_BlockedWrites(WriteBlockImageDispatch *dispatch) | |
25 | : dispatch(dispatch) { | |
26 | } | |
27 | ||
28 | void finish(int r) override { | |
29 | dispatch->handle_blocked_writes(r); | |
30 | } | |
31 | }; | |
32 | ||
33 | template <typename I> | |
34 | WriteBlockImageDispatch<I>::WriteBlockImageDispatch(I* image_ctx) | |
35 | : m_image_ctx(image_ctx), | |
36 | m_lock(ceph::make_shared_mutex( | |
37 | util::unique_lock_name("librbd::io::WriteBlockImageDispatch::m_lock", | |
38 | this))) { | |
39 | auto cct = m_image_ctx->cct; | |
40 | ldout(cct, 5) << "ictx=" << image_ctx << dendl; | |
41 | } | |
42 | ||
43 | template <typename I> | |
44 | void WriteBlockImageDispatch<I>::shut_down(Context* on_finish) { | |
45 | on_finish->complete(0); | |
46 | } | |
47 | ||
48 | template <typename I> | |
49 | int WriteBlockImageDispatch<I>::block_writes() { | |
50 | C_SaferCond cond_ctx; | |
51 | block_writes(&cond_ctx); | |
52 | return cond_ctx.wait(); | |
53 | } | |
54 | ||
55 | template <typename I> | |
56 | void WriteBlockImageDispatch<I>::block_writes(Context *on_blocked) { | |
57 | ceph_assert(ceph_mutex_is_locked(m_image_ctx->owner_lock)); | |
58 | auto cct = m_image_ctx->cct; | |
59 | ||
60 | // ensure owner lock is not held after block_writes completes | |
61 | on_blocked = util::create_async_context_callback( | |
62 | *m_image_ctx, on_blocked); | |
63 | ||
64 | { | |
65 | std::unique_lock locker{m_lock}; | |
66 | ++m_write_blockers; | |
67 | ldout(cct, 5) << m_image_ctx << ", " | |
68 | << "num=" << m_write_blockers << dendl; | |
69 | if (!m_write_blocker_contexts.empty() || m_in_flight_writes > 0) { | |
70 | ldout(cct, 5) << "waiting for in-flight writes to complete: " | |
71 | << "in_flight_writes=" << m_in_flight_writes << dendl; | |
72 | m_write_blocker_contexts.push_back(on_blocked); | |
73 | return; | |
74 | } | |
75 | } | |
76 | ||
77 | flush_io(on_blocked); | |
78 | }; | |
79 | ||
80 | template <typename I> | |
81 | void WriteBlockImageDispatch<I>::unblock_writes() { | |
82 | auto cct = m_image_ctx->cct; | |
83 | ||
84 | Contexts waiter_contexts; | |
85 | Contexts dispatch_contexts; | |
86 | { | |
87 | std::unique_lock locker{m_lock}; | |
88 | ceph_assert(m_write_blockers > 0); | |
89 | --m_write_blockers; | |
90 | ||
91 | ldout(cct, 5) << m_image_ctx << ", " | |
92 | << "num=" << m_write_blockers << dendl; | |
93 | if (m_write_blockers == 0) { | |
94 | std::swap(waiter_contexts, m_unblocked_write_waiter_contexts); | |
95 | std::swap(dispatch_contexts, m_on_dispatches); | |
96 | } | |
97 | } | |
98 | ||
99 | for (auto ctx : waiter_contexts) { | |
100 | ctx->complete(0); | |
101 | } | |
102 | ||
103 | for (auto ctx : dispatch_contexts) { | |
104 | ctx->complete(0); | |
105 | } | |
106 | } | |
107 | ||
108 | template <typename I> | |
109 | void WriteBlockImageDispatch<I>::wait_on_writes_unblocked( | |
110 | Context *on_unblocked) { | |
111 | ceph_assert(ceph_mutex_is_locked(m_image_ctx->owner_lock)); | |
112 | auto cct = m_image_ctx->cct; | |
113 | ||
114 | { | |
115 | std::unique_lock locker{m_lock}; | |
116 | ldout(cct, 20) << m_image_ctx << ", " | |
117 | << "write_blockers=" << m_write_blockers << dendl; | |
118 | if (!m_unblocked_write_waiter_contexts.empty() || m_write_blockers > 0) { | |
119 | m_unblocked_write_waiter_contexts.push_back(on_unblocked); | |
120 | return; | |
121 | } | |
122 | } | |
123 | ||
124 | on_unblocked->complete(0); | |
125 | } | |
126 | ||
127 | template <typename I> | |
128 | bool WriteBlockImageDispatch<I>::write( | |
129 | AioCompletion* aio_comp, Extents &&image_extents, bufferlist &&bl, | |
1e59de90 | 130 | int op_flags, const ZTracer::Trace &parent_trace, |
f67539c2 TL |
131 | uint64_t tid, std::atomic<uint32_t>* image_dispatch_flags, |
132 | DispatchResult* dispatch_result, Context** on_finish, | |
133 | Context* on_dispatched) { | |
134 | auto cct = m_image_ctx->cct; | |
135 | ldout(cct, 20) << "tid=" << tid << dendl; | |
136 | ||
137 | return process_io(tid, dispatch_result, on_finish, on_dispatched); | |
138 | } | |
139 | ||
140 | template <typename I> | |
141 | bool WriteBlockImageDispatch<I>::discard( | |
142 | AioCompletion* aio_comp, Extents &&image_extents, | |
1e59de90 TL |
143 | uint32_t discard_granularity_bytes, const ZTracer::Trace &parent_trace, |
144 | uint64_t tid, std::atomic<uint32_t>* image_dispatch_flags, | |
f67539c2 TL |
145 | DispatchResult* dispatch_result, Context** on_finish, |
146 | Context* on_dispatched) { | |
147 | auto cct = m_image_ctx->cct; | |
148 | ldout(cct, 20) << "tid=" << tid << dendl; | |
149 | ||
150 | return process_io(tid, dispatch_result, on_finish, on_dispatched); | |
151 | } | |
152 | ||
153 | template <typename I> | |
154 | bool WriteBlockImageDispatch<I>::write_same( | |
155 | AioCompletion* aio_comp, Extents &&image_extents, bufferlist &&bl, | |
1e59de90 | 156 | int op_flags, const ZTracer::Trace &parent_trace, |
f67539c2 TL |
157 | uint64_t tid, std::atomic<uint32_t>* image_dispatch_flags, |
158 | DispatchResult* dispatch_result, Context** on_finish, | |
159 | Context* on_dispatched) { | |
160 | auto cct = m_image_ctx->cct; | |
161 | ldout(cct, 20) << "tid=" << tid << dendl; | |
162 | ||
163 | return process_io(tid, dispatch_result, on_finish, on_dispatched); | |
164 | } | |
165 | ||
166 | template <typename I> | |
167 | bool WriteBlockImageDispatch<I>::compare_and_write( | |
1e59de90 TL |
168 | AioCompletion* aio_comp, Extents &&image_extents, |
169 | bufferlist &&cmp_bl, bufferlist &&bl, uint64_t *mismatch_offset, | |
170 | int op_flags, 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 << dendl; | |
176 | ||
177 | return process_io(tid, dispatch_result, on_finish, on_dispatched); | |
178 | } | |
179 | ||
180 | template <typename I> | |
181 | bool WriteBlockImageDispatch<I>::flush( | |
182 | AioCompletion* aio_comp, FlushSource flush_source, | |
183 | const ZTracer::Trace &parent_trace, uint64_t tid, | |
184 | std::atomic<uint32_t>* image_dispatch_flags, | |
185 | DispatchResult* dispatch_result, Context** on_finish, | |
186 | Context* on_dispatched) { | |
187 | auto cct = m_image_ctx->cct; | |
188 | ldout(cct, 20) << "tid=" << tid << dendl; | |
189 | ||
190 | if (flush_source != FLUSH_SOURCE_USER) { | |
191 | return false; | |
192 | } | |
193 | ||
194 | return process_io(tid, dispatch_result, on_finish, on_dispatched); | |
195 | } | |
196 | ||
197 | template <typename I> | |
198 | void WriteBlockImageDispatch<I>::handle_finished(int r, uint64_t tid) { | |
199 | auto cct = m_image_ctx->cct; | |
200 | ldout(cct, 20) << "r=" << r << ", tid=" << tid << dendl; | |
201 | ||
202 | std::unique_lock locker{m_lock}; | |
203 | ceph_assert(m_in_flight_writes > 0); | |
204 | --m_in_flight_writes; | |
205 | ||
206 | bool writes_blocked = false; | |
207 | if (m_write_blockers > 0 && m_in_flight_writes == 0) { | |
208 | ldout(cct, 10) << "flushing all in-flight IO for blocked writes" << dendl; | |
209 | writes_blocked = true; | |
210 | } | |
211 | locker.unlock(); | |
212 | ||
213 | if (writes_blocked) { | |
214 | flush_io(new C_BlockedWrites(this)); | |
215 | } | |
216 | } | |
217 | ||
218 | template <typename I> | |
219 | bool WriteBlockImageDispatch<I>::process_io( | |
220 | uint64_t tid, DispatchResult* dispatch_result, Context** on_finish, | |
221 | Context* on_dispatched) { | |
222 | std::unique_lock locker{m_lock}; | |
223 | if (m_write_blockers > 0 || !m_on_dispatches.empty()) { | |
224 | *dispatch_result = DISPATCH_RESULT_RESTART; | |
225 | m_on_dispatches.push_back(on_dispatched); | |
226 | return true; | |
227 | } | |
228 | ||
229 | ++m_in_flight_writes; | |
230 | *on_finish = new LambdaContext([this, tid, on_finish=*on_finish](int r) { | |
231 | handle_finished(r, tid); | |
232 | on_finish->complete(r); | |
233 | }); | |
234 | return false; | |
235 | } | |
236 | ||
237 | template <typename I> | |
238 | void WriteBlockImageDispatch<I>::flush_io(Context* on_finish) { | |
239 | auto cct = m_image_ctx->cct; | |
240 | ldout(cct, 10) << dendl; | |
241 | ||
242 | // ensure that all in-flight IO is flushed | |
243 | auto aio_comp = AioCompletion::create_and_start( | |
244 | on_finish, util::get_image_ctx(m_image_ctx), librbd::io::AIO_TYPE_FLUSH); | |
245 | auto req = ImageDispatchSpec::create_flush( | |
246 | *m_image_ctx, IMAGE_DISPATCH_LAYER_WRITE_BLOCK, aio_comp, | |
247 | FLUSH_SOURCE_WRITE_BLOCK, {}); | |
248 | req->send(); | |
249 | } | |
250 | ||
251 | template <typename I> | |
252 | void WriteBlockImageDispatch<I>::handle_blocked_writes(int r) { | |
253 | auto cct = m_image_ctx->cct; | |
254 | ldout(cct, 10) << dendl; | |
255 | ||
256 | Contexts write_blocker_contexts; | |
257 | { | |
258 | std::unique_lock locker{m_lock}; | |
259 | std::swap(write_blocker_contexts, m_write_blocker_contexts); | |
260 | } | |
261 | ||
262 | for (auto ctx : write_blocker_contexts) { | |
263 | ctx->complete(0); | |
264 | } | |
265 | } | |
266 | ||
267 | } // namespace io | |
268 | } // namespace librbd | |
269 | ||
270 | template class librbd::io::WriteBlockImageDispatch<librbd::ImageCtx>; |