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