]> git.proxmox.com Git - ceph.git/blame - ceph/src/librbd/operation/TrimRequest.cc
update sources to 12.2.10
[ceph.git] / ceph / src / librbd / operation / TrimRequest.cc
CommitLineData
7c673cae
FG
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/operation/TrimRequest.h"
5#include "librbd/AsyncObjectThrottle.h"
6#include "librbd/ExclusiveLock.h"
7#include "librbd/ImageCtx.h"
8#include "librbd/internal.h"
9#include "librbd/ObjectMap.h"
10#include "librbd/Utils.h"
11#include "librbd/io/ObjectRequest.h"
12#include "common/ContextCompletion.h"
13#include "common/dout.h"
14#include "common/errno.h"
15#include "osdc/Striper.h"
16
17#include <boost/bind.hpp>
18#include <boost/lambda/bind.hpp>
19#include <boost/lambda/construct.hpp>
20#include <boost/scope_exit.hpp>
21
22#define dout_subsys ceph_subsys_rbd
23#undef dout_prefix
24#define dout_prefix *_dout << "librbd::TrimRequest: "
25
26namespace librbd {
27namespace operation {
28
29template <typename I>
30class C_CopyupObject : public C_AsyncObjectThrottle<I> {
31public:
32 C_CopyupObject(AsyncObjectThrottle<I> &throttle, I *image_ctx,
33 ::SnapContext snapc, uint64_t object_no)
34 : C_AsyncObjectThrottle<I>(throttle, *image_ctx), m_snapc(snapc),
35 m_object_no(object_no)
36 {
37 }
38
39 int send() override {
40 I &image_ctx = this->m_image_ctx;
41 assert(image_ctx.owner_lock.is_locked());
42 assert(image_ctx.exclusive_lock == nullptr ||
43 image_ctx.exclusive_lock->is_lock_owner());
44
45 string oid = image_ctx.get_object_name(m_object_no);
46 ldout(image_ctx.cct, 10) << "removing (with copyup) " << oid << dendl;
47
b32b8144
FG
48 auto req = io::ObjectRequest<I>::create_discard(
49 &image_ctx, oid, m_object_no, 0, image_ctx.layout.object_size, m_snapc,
50 false, false, {}, this);
7c673cae
FG
51 req->send();
52 return 0;
53 }
54private:
55 ::SnapContext m_snapc;
56 uint64_t m_object_no;
57};
58
59template <typename I>
60class C_RemoveObject : public C_AsyncObjectThrottle<I> {
61public:
3efd9988 62 C_RemoveObject(AsyncObjectThrottle<I> &throttle, I *image_ctx,
7c673cae
FG
63 uint64_t object_no)
64 : C_AsyncObjectThrottle<I>(throttle, *image_ctx), m_object_no(object_no)
65 {
66 }
67
68 int send() override {
69 I &image_ctx = this->m_image_ctx;
70 assert(image_ctx.owner_lock.is_locked());
71 assert(image_ctx.exclusive_lock == nullptr ||
72 image_ctx.exclusive_lock->is_lock_owner());
73
74 {
75 RWLock::RLocker snap_locker(image_ctx.snap_lock);
76 if (image_ctx.object_map != nullptr &&
77 !image_ctx.object_map->object_may_exist(m_object_no)) {
78 return 1;
79 }
80 }
81
82 string oid = image_ctx.get_object_name(m_object_no);
83 ldout(image_ctx.cct, 10) << "removing " << oid << dendl;
84
85 librados::AioCompletion *rados_completion =
86 util::create_rados_callback(this);
87 int r = image_ctx.data_ctx.aio_remove(oid, rados_completion);
88 assert(r == 0);
89 rados_completion->release();
90 return 0;
91 }
92
93private:
94 uint64_t m_object_no;
95};
96
97template <typename I>
98TrimRequest<I>::TrimRequest(I &image_ctx, Context *on_finish,
99 uint64_t original_size, uint64_t new_size,
100 ProgressContext &prog_ctx)
101 : AsyncRequest<I>(image_ctx, on_finish), m_new_size(new_size),
102 m_prog_ctx(prog_ctx)
103{
104 uint64_t period = image_ctx.get_stripe_period();
105 uint64_t new_num_periods = ((m_new_size + period - 1) / period);
106 m_delete_off = MIN(new_num_periods * period, original_size);
107 // first object we can delete free and clear
108 m_delete_start = new_num_periods * image_ctx.get_stripe_count();
3efd9988 109 m_delete_start_min = m_delete_start;
7c673cae
FG
110 m_num_objects = Striper::get_num_objects(image_ctx.layout, original_size);
111
112 CephContext *cct = image_ctx.cct;
113 ldout(cct, 10) << this << " trim image " << original_size << " -> "
114 << m_new_size << " periods " << new_num_periods
115 << " discard to offset " << m_delete_off
116 << " delete objects " << m_delete_start
117 << " to " << m_num_objects << dendl;
118}
119
120template <typename I>
121bool TrimRequest<I>::should_complete(int r)
122{
123 I &image_ctx = this->m_image_ctx;
124 CephContext *cct = image_ctx.cct;
125 ldout(cct, 5) << this << " should_complete: r=" << r << dendl;
126 if (r == -ERESTART) {
127 ldout(cct, 5) << "trim operation interrupted" << dendl;
128 return true;
129 } else if (r < 0) {
130 lderr(cct) << "trim encountered an error: " << cpp_strerror(r) << dendl;
131 return true;
132 }
133
134 RWLock::RLocker owner_lock(image_ctx.owner_lock);
135 switch (m_state) {
3efd9988
FG
136 case STATE_PRE_TRIM:
137 ldout(cct, 5) << " PRE_TRIM" << dendl;
7c673cae
FG
138 send_copyup_objects();
139 break;
140
141 case STATE_COPYUP_OBJECTS:
142 ldout(cct, 5) << " COPYUP_OBJECTS" << dendl;
7c673cae
FG
143 send_remove_objects();
144 break;
145
146 case STATE_REMOVE_OBJECTS:
147 ldout(cct, 5) << " REMOVE_OBJECTS" << dendl;
3efd9988 148 send_post_trim();
7c673cae
FG
149 break;
150
3efd9988
FG
151 case STATE_POST_TRIM:
152 ldout(cct, 5) << " POST_TRIM" << dendl;
7c673cae
FG
153 send_clean_boundary();
154 break;
155
156 case STATE_CLEAN_BOUNDARY:
157 ldout(cct, 5) << "CLEAN_BOUNDARY" << dendl;
158 send_finish(0);
159 break;
160
161 case STATE_FINISHED:
162 ldout(cct, 5) << "FINISHED" << dendl;
163 return true;
164
165 default:
166 lderr(cct) << "invalid state: " << m_state << dendl;
167 assert(false);
168 break;
169 }
170 return false;
171}
172
173template <typename I>
174void TrimRequest<I>::send() {
3efd9988 175 send_pre_trim();
7c673cae
FG
176}
177
178template<typename I>
3efd9988 179void TrimRequest<I>::send_pre_trim() {
7c673cae
FG
180 I &image_ctx = this->m_image_ctx;
181 assert(image_ctx.owner_lock.is_locked());
182
3efd9988
FG
183 if (m_delete_start >= m_num_objects) {
184 send_clean_boundary();
185 return;
186 }
7c673cae 187
7c673cae
FG
188 {
189 RWLock::RLocker snap_locker(image_ctx.snap_lock);
3efd9988
FG
190 if (image_ctx.object_map != nullptr) {
191 ldout(image_ctx.cct, 5) << this << " send_pre_trim: "
192 << " delete_start_min=" << m_delete_start_min
193 << " num_objects=" << m_num_objects << dendl;
194 m_state = STATE_PRE_TRIM;
7c673cae 195
3efd9988 196 assert(image_ctx.exclusive_lock->is_lock_owner());
7c673cae 197
3efd9988
FG
198 RWLock::WLocker object_map_locker(image_ctx.object_map_lock);
199 if (image_ctx.object_map->template aio_update<AsyncRequest<I> >(
200 CEPH_NOSNAP, m_delete_start_min, m_num_objects, OBJECT_PENDING,
91327a77 201 OBJECT_EXISTS, {}, false, this)) {
3efd9988
FG
202 return;
203 }
204 }
205 }
7c673cae 206
3efd9988 207 send_copyup_objects();
7c673cae
FG
208}
209
210template<typename I>
3efd9988 211void TrimRequest<I>::send_copyup_objects() {
7c673cae
FG
212 I &image_ctx = this->m_image_ctx;
213 assert(image_ctx.owner_lock.is_locked());
214
3efd9988 215 ::SnapContext snapc;
7c673cae
FG
216 bool has_snapshots;
217 uint64_t parent_overlap;
218 {
219 RWLock::RLocker snap_locker(image_ctx.snap_lock);
220 RWLock::RLocker parent_locker(image_ctx.parent_lock);
221
3efd9988 222 snapc = image_ctx.snapc;
7c673cae
FG
223 has_snapshots = !image_ctx.snaps.empty();
224 int r = image_ctx.get_parent_overlap(CEPH_NOSNAP, &parent_overlap);
225 assert(r == 0);
226 }
227
228 // copyup is only required for portion of image that overlaps parent
3efd9988
FG
229 uint64_t copyup_end = Striper::get_num_objects(image_ctx.layout,
230 parent_overlap);
7c673cae
FG
231
232 // TODO: protect against concurrent shrink and snap create?
233 // skip to remove if no copyup is required.
3efd9988
FG
234 if (copyup_end <= m_delete_start || !has_snapshots) {
235 send_remove_objects();
7c673cae
FG
236 return;
237 }
238
3efd9988
FG
239 uint64_t copyup_start = m_delete_start;
240 m_delete_start = copyup_end;
7c673cae 241
3efd9988
FG
242 ldout(image_ctx.cct, 5) << this << " send_copyup_objects: "
243 << " start object=" << copyup_start << ", "
244 << " end object=" << copyup_end << dendl;
245 m_state = STATE_COPYUP_OBJECTS;
7c673cae 246
3efd9988
FG
247 Context *ctx = this->create_callback_context();
248 typename AsyncObjectThrottle<I>::ContextFactory context_factory(
249 boost::lambda::bind(boost::lambda::new_ptr<C_CopyupObject<I> >(),
250 boost::lambda::_1, &image_ctx, snapc, boost::lambda::_2));
251 AsyncObjectThrottle<I> *throttle = new AsyncObjectThrottle<I>(
252 this, image_ctx, context_factory, ctx, &m_prog_ctx, copyup_start,
253 copyup_end);
254 throttle->start_ops(image_ctx.concurrent_management_ops);
7c673cae
FG
255}
256
257template <typename I>
3efd9988 258void TrimRequest<I>::send_remove_objects() {
7c673cae
FG
259 I &image_ctx = this->m_image_ctx;
260 assert(image_ctx.owner_lock.is_locked());
7c673cae 261
3efd9988
FG
262 ldout(image_ctx.cct, 5) << this << " send_remove_objects: "
263 << " delete_start=" << m_delete_start
264 << " num_objects=" << m_num_objects << dendl;
265 m_state = STATE_REMOVE_OBJECTS;
7c673cae 266
3efd9988
FG
267 Context *ctx = this->create_callback_context();
268 typename AsyncObjectThrottle<I>::ContextFactory context_factory(
269 boost::lambda::bind(boost::lambda::new_ptr<C_RemoveObject<I> >(),
270 boost::lambda::_1, &image_ctx, boost::lambda::_2));
271 AsyncObjectThrottle<I> *throttle = new AsyncObjectThrottle<I>(
272 this, image_ctx, context_factory, ctx, &m_prog_ctx, m_delete_start,
273 m_num_objects);
274 throttle->start_ops(image_ctx.concurrent_management_ops);
7c673cae
FG
275}
276
277template<typename I>
3efd9988 278void TrimRequest<I>::send_post_trim() {
7c673cae
FG
279 I &image_ctx = this->m_image_ctx;
280 assert(image_ctx.owner_lock.is_locked());
281
282 {
283 RWLock::RLocker snap_locker(image_ctx.snap_lock);
284 if (image_ctx.object_map != nullptr) {
3efd9988
FG
285 ldout(image_ctx.cct, 5) << this << " send_post_trim:"
286 << " delete_start_min=" << m_delete_start_min
287 << " num_objects=" << m_num_objects << dendl;
288 m_state = STATE_POST_TRIM;
7c673cae
FG
289
290 assert(image_ctx.exclusive_lock->is_lock_owner());
291
7c673cae
FG
292 RWLock::WLocker object_map_locker(image_ctx.object_map_lock);
293 if (image_ctx.object_map->template aio_update<AsyncRequest<I> >(
3efd9988 294 CEPH_NOSNAP, m_delete_start_min, m_num_objects, OBJECT_NONEXISTENT,
91327a77 295 OBJECT_PENDING, {}, false, this)) {
7c673cae
FG
296 return;
297 }
298 }
299 }
300
7c673cae
FG
301 send_clean_boundary();
302}
303
304template <typename I>
305void TrimRequest<I>::send_clean_boundary() {
306 I &image_ctx = this->m_image_ctx;
307 assert(image_ctx.owner_lock.is_locked());
308 CephContext *cct = image_ctx.cct;
309 if (m_delete_off <= m_new_size) {
310 send_finish(0);
311 return;
312 }
313
314 // should have been canceled prior to releasing lock
315 assert(image_ctx.exclusive_lock == nullptr ||
316 image_ctx.exclusive_lock->is_lock_owner());
317 uint64_t delete_len = m_delete_off - m_new_size;
318 ldout(image_ctx.cct, 5) << this << " send_clean_boundary: "
319 << " delete_off=" << m_delete_off
320 << " length=" << delete_len << dendl;
321 m_state = STATE_CLEAN_BOUNDARY;
322
323 ::SnapContext snapc;
324 {
325 RWLock::RLocker snap_locker(image_ctx.snap_lock);
326 snapc = image_ctx.snapc;
327 }
328
329 // discard the weird boundary
330 std::vector<ObjectExtent> extents;
331 Striper::file_to_extents(cct, image_ctx.format_string,
332 &image_ctx.layout, m_new_size, delete_len, 0,
333 extents);
334
335 ContextCompletion *completion =
336 new ContextCompletion(this->create_async_callback_context(), true);
337 for (vector<ObjectExtent>::iterator p = extents.begin();
338 p != extents.end(); ++p) {
339 ldout(cct, 20) << " ex " << *p << dendl;
340 Context *req_comp = new C_ContextCompletion(*completion);
341
7c673cae 342 if (p->offset == 0) {
b32b8144
FG
343 // treat as a full object delete on the boundary
344 p->length = image_ctx.layout.object_size;
7c673cae 345 }
b32b8144
FG
346 auto req = io::ObjectRequest<I>::create_discard(&image_ctx, p->oid.name,
347 p->objectno, p->offset,
348 p->length, snapc, false,
349 true, {}, req_comp);
7c673cae
FG
350 req->send();
351 }
352 completion->finish_adding_requests();
353}
354
355template <typename I>
356void TrimRequest<I>::send_finish(int r) {
357 m_state = STATE_FINISHED;
358 this->async_complete(r);
359}
360
361} // namespace operation
362} // namespace librbd
363
364template class librbd::operation::TrimRequest<librbd::ImageCtx>;