]> git.proxmox.com Git - ceph.git/blob - ceph/src/mds/ScrubStack.h
da037cabbab2541042e46a4aee5b763334c9ec73
[ceph.git] / ceph / src / mds / ScrubStack.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2014 Red Hat
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #ifndef SCRUBSTACK_H_
16 #define SCRUBSTACK_H_
17
18 #include "CDir.h"
19 #include "CDentry.h"
20 #include "CInode.h"
21 #include "MDSContext.h"
22 #include "ScrubHeader.h"
23
24 #include "common/LogClient.h"
25 #include "include/elist.h"
26
27 class MDCache;
28 class Finisher;
29
30 class ScrubStack {
31 public:
32 ScrubStack(MDCache *mdc, LogChannelRef &clog, Finisher *finisher_) :
33 mdcache(mdc),
34 clog(clog),
35 finisher(finisher_),
36 inode_stack(member_offset(CInode, item_scrub)),
37 scrubstack(this),
38 scrub_kick(mdc, this) {}
39 ~ScrubStack() {
40 ceph_assert(inode_stack.empty());
41 ceph_assert(!scrubs_in_progress);
42 }
43 /**
44 * Put a inode on the top of the scrub stack, so it is the highest priority.
45 * If there are other scrubs in progress, they will not continue scrubbing new
46 * entries until this one is completed.
47 * @param in The inodey to scrub
48 * @param header The ScrubHeader propagated from wherever this scrub
49 * was initiated
50 */
51 void enqueue_inode_top(CInode *in, ScrubHeaderRef& header,
52 MDSContext *on_finish) {
53 enqueue_inode(in, header, on_finish, true);
54 scrub_origins.emplace(in);
55 clog_scrub_summary(in);
56 }
57 /** Like enqueue_inode_top, but we wait for all pending scrubs before
58 * starting this one.
59 */
60 void enqueue_inode_bottom(CInode *in, ScrubHeaderRef& header,
61 MDSContext *on_finish) {
62 enqueue_inode(in, header, on_finish, false);
63 scrub_origins.emplace(in);
64 clog_scrub_summary(in);
65 }
66
67 /**
68 * Abort an ongoing scrub operation. The abort operation could be
69 * delayed if there are in-progress scrub operations on going. The
70 * caller should provide a context which is completed after all
71 * in-progress scrub operations are completed and pending inodes
72 * are removed from the scrub stack (with the context callbacks for
73 * inodes completed with -ECANCELED).
74 * @param on_finish Context callback to invoke after abort
75 */
76 void scrub_abort(Context *on_finish);
77
78 /**
79 * Pause scrub operations. Similar to abort, pause is delayed if
80 * there are in-progress scrub operations on going. The caller
81 * should provide a context which is completed after all in-progress
82 * scrub operations are completed. Subsequent scrub operations are
83 * queued until scrub is resumed.
84 * @param on_finish Context callback to invoke after pause
85 */
86 void scrub_pause(Context *on_finish);
87
88 /**
89 * Resume a paused scrub. Unlike abort or pause, this is instantaneous.
90 * Pending pause operations are cancelled (context callbacks are
91 * invoked with -ECANCELED).
92 * @returns 0 (success) if resumed, -EINVAL if an abort is in-progress.
93 */
94 bool scrub_resume();
95
96 /**
97 * Get the current scrub status as human readable string. Some basic
98 * information is returned such as number of inodes pending abort/pause.
99 */
100 void scrub_status(Formatter *f);
101
102 /**
103 * Get a high level scrub status summary such as current scrub state
104 * and scrub paths.
105 */
106 std::string_view scrub_summary();
107
108 bool is_scrubbing() const { return !inode_stack.empty(); }
109
110 MDCache *mdcache;
111
112 protected:
113 class C_KickOffScrubs : public MDSInternalContext {
114 public:
115 C_KickOffScrubs(MDCache *mdcache, ScrubStack *s);
116 void finish(int r) override { }
117 void complete(int r) override {
118 if (r == -ECANCELED) {
119 return;
120 }
121
122 stack->scrubs_in_progress--;
123 stack->kick_off_scrubs();
124 // don't delete self
125 }
126 private:
127 ScrubStack *stack;
128 };
129
130 // reference to global cluster log client
131 LogChannelRef &clog;
132
133 /// A finisher needed so that we don't re-enter kick_off_scrubs
134 Finisher *finisher;
135
136 /// The stack of inodes we want to scrub
137 elist<CInode*> inode_stack;
138 /// current number of dentries we're actually scrubbing
139 int scrubs_in_progress = 0;
140 ScrubStack *scrubstack; // hack for dout
141 int stack_size = 0;
142
143 C_KickOffScrubs scrub_kick;
144
145 private:
146 // scrub abort is _not_ a state, rather it's an operation that's
147 // performed after in-progress scrubs are finished.
148 enum State {
149 STATE_RUNNING = 0,
150 STATE_IDLE,
151 STATE_PAUSING,
152 STATE_PAUSED,
153 };
154 friend std::ostream &operator<<(std::ostream &os, const State &state);
155
156 friend class C_InodeValidated;
157
158 /**
159 * Put the inode at either the top or bottom of the stack, with
160 * the given scrub params, and then try and kick off more scrubbing.
161 */
162 void enqueue_inode(CInode *in, ScrubHeaderRef& header,
163 MDSContext *on_finish, bool top);
164 void _enqueue_inode(CInode *in, CDentry *parent, ScrubHeaderRef& header,
165 MDSContext *on_finish, bool top);
166 /**
167 * Kick off as many scrubs as are appropriate, based on the current
168 * state of the stack.
169 */
170 void kick_off_scrubs();
171 /**
172 * Push a inode on top of the stack.
173 */
174 inline void push_inode(CInode *in);
175 /**
176 * Push a inode to the bottom of the stack.
177 */
178 inline void push_inode_bottom(CInode *in);
179 /**
180 * Pop the given inode off the stack.
181 */
182 inline void pop_inode(CInode *in);
183
184 /**
185 * Scrub a file inode.
186 * @param in The inode to scrub
187 */
188 void scrub_file_inode(CInode *in);
189
190 /**
191 * Callback from completion of CInode::validate_disk_state
192 * @param in The inode we were validating
193 * @param r The return status from validate_disk_state
194 * @param result Populated results from validate_disk_state
195 */
196 void _validate_inode_done(CInode *in, int r,
197 const CInode::validated_data &result);
198
199 /**
200 * Make progress on scrubbing a directory-representing dirfrag and
201 * its children..
202 *
203 * 1) Select the next dirfrag which hasn't been scrubbed, and make progress
204 * on it if possible.
205 *
206 * 2) If not, move on to the next dirfrag and start it up, if any.
207 *
208 * 3) If waiting for results from dirfrag scrubs, do nothing.
209 *
210 * 4) If all dirfrags have been scrubbed, scrub my inode.
211 *
212 * @param in The CInode to scrub as a directory
213 * @param added_children set to true if we pushed some of our children
214 * onto the ScrubStack
215 * @param is_terminal set to true if there are no descendant dentries
216 * remaining to start scrubbing.
217 * @param done set to true if we and all our children have finished scrubbing
218 */
219 void scrub_dir_inode(CInode *in, bool *added_children, bool *is_terminal,
220 bool *done);
221 /**
222 * Make progress on scrubbing a dirfrag. It may return after each of the
223 * following steps, but will report making progress on each one.
224 *
225 * 1) enqueues the next unscrubbed child directory dentry at the
226 * top of the stack.
227 *
228 * 2) Initiates a scrub on the next unscrubbed file dentry
229 *
230 * If there are scrubs currently in progress on child dentries, no more child
231 * dentries to scrub, and this function is invoked, it will report no
232 * progress. Try again later.
233 *
234 */
235 void scrub_dirfrag(CDir *dir, ScrubHeaderRef& header,
236 bool *added_children, bool *is_terminal, bool *done);
237 /**
238 * Scrub a directory-representing dentry.
239 *
240 * @param in The directory inode we're doing final scrub on.
241 */
242 void scrub_dir_inode_final(CInode *in);
243
244 /**
245 * Get a CDir into memory, and return it if it's already complete.
246 * Otherwise, fetch it and kick off scrubbing when done.
247 *
248 * @param in The Inode to get the next directory from
249 * @param new_dir The CDir we're returning to you. NULL if
250 * not ready yet or there aren't any.
251 * @returns false if you have to wait, true if there's no work
252 * left to do (we returned it, or there are none left in this inode).
253 */
254 bool get_next_cdir(CInode *in, CDir **new_dir);
255
256 /**
257 * Set scrub state
258 * @param next_state State to move the scrub to.
259 */
260 void set_state(State next_state);
261
262 /**
263 * Is scrub in one of transition states (running, pausing)
264 */
265 bool scrub_in_transition_state();
266
267 /**
268 * complete queued up contexts
269 * @param r return value to complete contexts.
270 */
271 void complete_control_contexts(int r);
272
273 /**
274 * Abort pending scrubs for inodes waiting in the inode stack.
275 * Completion context is complete with -ECANCELED.
276 */
277 void abort_pending_scrubs();
278
279 /**
280 * Return path for a given inode.
281 * @param in inode to make path entry.
282 */
283 std::string scrub_inode_path(CInode *in) {
284 std::string path;
285 in->make_path_string(path, true);
286 return (path.empty() ? "/" : path.c_str());
287 }
288
289 /**
290 * Send scrub information (queued/finished scrub path and summary)
291 * to cluster log.
292 * @param in inode for which scrub has been queued or finished.
293 */
294 void clog_scrub_summary(CInode *in=nullptr);
295
296 State state = STATE_IDLE;
297 bool clear_inode_stack = false;
298
299 // list of pending context completions for asynchronous scrub
300 // control operations.
301 std::vector<Context *> control_ctxs;
302
303 // list of inodes for which scrub operations are running -- used
304 // to diplay out in `scrub status`.
305 std::set<CInode *> scrub_origins;
306 };
307
308 #endif /* SCRUBSTACK_H_ */