]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
06fbdda9 | 2 | |
6b803e5a | 3 | #include "libxfs.h" |
06fbdda9 | 4 | #include "globals.h" |
2556c98b | 5 | #include "progress.h" |
06fbdda9 MV |
6 | #include "err_protos.h" |
7 | #include <signal.h> | |
8 | ||
9 | #define ONEMINUTE 60 | |
10 | #define ONEHOUR (60*ONEMINUTE) | |
11 | #define ONEDAY (24*ONEHOUR) | |
12 | #define ONEWEEK (7*ONEDAY) | |
13 | ||
14 | static | |
15 | char *rpt_types[] = { | |
16 | #define TYPE_INODE 0 | |
97294b22 | 17 | N_("inodes"), |
06fbdda9 | 18 | #define TYPE_BLOCK 1 |
97294b22 | 19 | N_("blocks"), |
06fbdda9 | 20 | #define TYPE_DIR 2 |
97294b22 | 21 | N_("directories"), |
06fbdda9 | 22 | #define TYPE_AG 3 |
97294b22 | 23 | N_("allocation groups"), |
06fbdda9 | 24 | #define TYPE_AGI_BUCKET 4 |
97294b22 | 25 | N_("AGI unlinked buckets"), |
06fbdda9 | 26 | #define TYPE_EXTENTS 5 |
97294b22 | 27 | N_("extents"), |
06fbdda9 | 28 | #define TYPE_RTEXTENTS 6 |
97294b22 | 29 | N_("realtime extents"), |
06fbdda9 | 30 | #define TYPE_UNLINKED_LIST 7 |
97294b22 | 31 | N_("unlinked lists") |
06fbdda9 MV |
32 | }; |
33 | ||
34 | ||
35 | static | |
36 | char *rpt_fmts[] = { | |
37 | #define FMT1 0 | |
97294b22 | 38 | N_(" - %02d:%02d:%02d: %s - %llu of %llu %s done\n"), |
06fbdda9 | 39 | #define FMT2 1 |
97294b22 | 40 | N_(" - %02d:%02d:%02d: %s - %llu %s done\n"), |
06fbdda9 MV |
41 | }; |
42 | ||
43 | typedef struct progress_rpt_s { | |
44 | short format; | |
45 | char *msg; | |
46 | char **fmt; | |
47 | char **type; | |
48 | } progress_rpt_t; | |
49 | ||
50 | static | |
51 | progress_rpt_t progress_rpt_reports[] = { | |
97294b22 | 52 | {FMT1, N_("scanning filesystem freespace"), /* 0 */ |
06fbdda9 | 53 | &rpt_fmts[FMT1], &rpt_types[TYPE_AG]}, |
97294b22 | 54 | {FMT1, N_("scanning agi unlinked lists"), /* 1 */ |
06fbdda9 | 55 | &rpt_fmts[FMT1], &rpt_types[TYPE_AG]}, |
97294b22 | 56 | {FMT2, N_("check uncertain AG inodes"), /* 2 */ |
06fbdda9 | 57 | &rpt_fmts[FMT2], &rpt_types[TYPE_AGI_BUCKET]}, |
97294b22 | 58 | {FMT1, N_("process known inodes and inode discovery"), /* 3 */ |
06fbdda9 | 59 | &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]}, |
97294b22 | 60 | {FMT1, N_("process newly discovered inodes"), /* 4 */ |
06fbdda9 | 61 | &rpt_fmts[FMT1], &rpt_types[TYPE_AG]}, |
97294b22 | 62 | {FMT1, N_("setting up duplicate extent list"), /* 5 */ |
06fbdda9 | 63 | &rpt_fmts[FMT1], &rpt_types[TYPE_AG]}, |
97294b22 | 64 | {FMT1, N_("initialize realtime bitmap"), /* 6 */ |
06fbdda9 | 65 | &rpt_fmts[FMT1], &rpt_types[TYPE_BLOCK]}, |
97294b22 | 66 | {FMT1, N_("reset realtime bitmaps"), /* 7 */ |
06fbdda9 | 67 | &rpt_fmts[FMT1], &rpt_types[TYPE_AG]}, |
97294b22 | 68 | {FMT1, N_("check for inodes claiming duplicate blocks"), /* 8 */ |
06fbdda9 | 69 | &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]}, |
97294b22 | 70 | {FMT1, N_("rebuild AG headers and trees"), /* 9 */ |
06fbdda9 | 71 | &rpt_fmts[FMT1], &rpt_types[TYPE_AG]}, |
97294b22 | 72 | {FMT1, N_("traversing filesystem"), /* 10 */ |
06fbdda9 | 73 | &rpt_fmts[FMT1], &rpt_types[TYPE_AG]}, |
97294b22 | 74 | {FMT2, N_("traversing all unattached subtrees"), /* 11 */ |
06fbdda9 | 75 | &rpt_fmts[FMT2], &rpt_types[TYPE_DIR]}, |
97294b22 | 76 | {FMT2, N_("moving disconnected inodes to lost+found"), /* 12 */ |
06fbdda9 | 77 | &rpt_fmts[FMT2], &rpt_types[TYPE_INODE]}, |
97294b22 | 78 | {FMT1, N_("verify and correct link counts"), /* 13 */ |
e161d4a8 | 79 | &rpt_fmts[FMT1], &rpt_types[TYPE_AG]}, |
97294b22 | 80 | {FMT1, N_("verify link counts"), /* 14 */ |
e161d4a8 | 81 | &rpt_fmts[FMT1], &rpt_types[TYPE_AG]} |
06fbdda9 MV |
82 | }; |
83 | ||
00ff2b10 | 84 | static pthread_t report_thread; |
06fbdda9 MV |
85 | |
86 | typedef struct msg_block_s { | |
87 | pthread_mutex_t mutex; | |
88 | progress_rpt_t *format; | |
14f8b681 DW |
89 | uint64_t *done; |
90 | uint64_t *total; | |
06fbdda9 MV |
91 | int count; |
92 | int interval; | |
93 | } msg_block_t; | |
94 | static msg_block_t global_msgs; | |
95 | ||
96 | typedef struct phase_times_s { | |
97 | time_t start; | |
98 | time_t end; | |
99 | time_t duration; | |
14f8b681 | 100 | uint64_t item_counts[4]; |
06fbdda9 MV |
101 | } phase_times_t; |
102 | static phase_times_t phase_times[8]; | |
103 | ||
104 | static void *progress_rpt_thread(void *); | |
105 | static int current_phase; | |
106 | static int running; | |
14f8b681 | 107 | static uint64_t prog_rpt_total; |
06fbdda9 MV |
108 | |
109 | void | |
110 | init_progress_rpt (void) | |
111 | { | |
112 | ||
113 | /* | |
114 | * allocate the done vector | |
115 | */ | |
116 | ||
14f8b681 DW |
117 | if ((prog_rpt_done = (uint64_t *) |
118 | malloc(sizeof(uint64_t)*glob_agcount)) == NULL) { | |
06fbdda9 MV |
119 | do_error(_("cannot malloc pointer to done vector\n")); |
120 | } | |
14f8b681 | 121 | bzero(prog_rpt_done, sizeof(uint64_t)*glob_agcount); |
06fbdda9 MV |
122 | |
123 | /* | |
124 | * Setup comm block, start the thread | |
125 | */ | |
126 | ||
127 | pthread_mutex_init(&global_msgs.mutex, NULL); | |
7f2d6b81 | 128 | global_msgs.format = NULL; |
06fbdda9 MV |
129 | global_msgs.count = glob_agcount; |
130 | global_msgs.interval = report_interval; | |
131 | global_msgs.done = prog_rpt_done; | |
132 | global_msgs.total = &prog_rpt_total; | |
133 | ||
134 | if (pthread_create (&report_thread, NULL, | |
135 | progress_rpt_thread, (void *)&global_msgs)) | |
136 | do_error(_("unable to create progress report thread\n")); | |
137 | ||
138 | return; | |
139 | } | |
140 | ||
141 | void | |
142 | stop_progress_rpt(void) | |
143 | { | |
144 | ||
145 | /* | |
146 | * Tell msg thread to shutdown, | |
147 | * wait for all threads to finished | |
148 | */ | |
149 | ||
150 | running = 0; | |
151 | pthread_kill (report_thread, SIGHUP); | |
152 | pthread_join (report_thread, NULL); | |
153 | free(prog_rpt_done); | |
154 | return; | |
155 | } | |
156 | ||
157 | static void * | |
158 | progress_rpt_thread (void *p) | |
159 | { | |
160 | ||
161 | int i; | |
162 | int caught; | |
163 | sigset_t sigs_to_catch; | |
164 | struct tm *tmp; | |
165 | time_t now, elapsed; | |
166 | timer_t timerid; | |
167 | struct itimerspec timespec; | |
168 | char *msgbuf; | |
14f8b681 DW |
169 | uint64_t *donep; |
170 | uint64_t sum; | |
06fbdda9 | 171 | msg_block_t *msgp = (msg_block_t *)p; |
14f8b681 | 172 | uint64_t percent; |
06fbdda9 | 173 | |
7f2d6b81 ES |
174 | /* It's possible to get here very early w/ no progress msg set */ |
175 | if (!msgp->format) | |
176 | return NULL; | |
177 | ||
06fbdda9 MV |
178 | if ((msgbuf = (char *)malloc(DURATION_BUF_SIZE)) == NULL) |
179 | do_error (_("progress_rpt: cannot malloc progress msg buffer\n")); | |
180 | ||
181 | running = 1; | |
182 | ||
183 | /* | |
184 | * Specify a repeating timer that fires each MSG_INTERVAL seconds. | |
185 | */ | |
2556c98b | 186 | |
07b010a0 | 187 | memset(×pec, 0, sizeof(timespec)); |
06fbdda9 | 188 | timespec.it_value.tv_sec = msgp->interval; |
06fbdda9 | 189 | timespec.it_interval.tv_sec = msgp->interval; |
06fbdda9 MV |
190 | |
191 | if (timer_create (CLOCK_REALTIME, NULL, &timerid)) | |
192 | do_error(_("progress_rpt: cannot create timer\n")); | |
193 | ||
194 | if (timer_settime (timerid, 0, ×pec, NULL)) | |
195 | do_error(_("progress_rpt: cannot set timer\n")); | |
196 | ||
197 | /* | |
198 | * Main loop - output messages based on periodic signal arrival | |
199 | * set this thread's signal mask to block out all other signals | |
200 | */ | |
201 | ||
202 | sigemptyset (&sigs_to_catch); | |
203 | sigaddset (&sigs_to_catch, SIGALRM); | |
204 | sigaddset (&sigs_to_catch, SIGHUP); | |
205 | sigwait (&sigs_to_catch, &caught); | |
206 | ||
207 | while (caught != SIGHUP) { | |
208 | /* | |
209 | * Allow the mainline to hold off messages by holding | |
210 | * the lock. We don't want to just skip a period in case the | |
211 | * reporting interval is very long... people get nervous. But, | |
212 | * if the interval is very short, we can't let the timer go | |
213 | * off again without sigwait'ing for it. So disarm the timer | |
214 | * while we try to get the lock and giveup the cpu... the | |
215 | * mainline shouldn't take that long. | |
216 | */ | |
217 | ||
218 | if (pthread_mutex_lock(&msgp->mutex)) { | |
219 | do_error(_("progress_rpt: cannot lock progress mutex\n")); | |
220 | } | |
221 | ||
222 | if (!running) | |
223 | break; | |
224 | ||
225 | now = time (NULL); | |
226 | tmp = localtime ((const time_t *) &now); | |
227 | ||
228 | /* | |
229 | * Sum the work | |
230 | */ | |
231 | ||
232 | sum = 0; | |
233 | donep = msgp->done; | |
234 | for (i = 0; i < msgp->count; i++) { | |
235 | sum += *donep++; | |
236 | } | |
237 | ||
238 | percent = 0; | |
239 | switch(msgp->format->format) { | |
240 | case FMT1: | |
241 | if (*msgp->total) | |
242 | percent = (sum * 100) / ( *msgp->total ); | |
243 | sprintf (msgbuf, *msgp->format->fmt, | |
244 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, | |
245 | msgp->format->msg, sum, | |
246 | *msgp->total, *msgp->format->type); | |
247 | break; | |
248 | case FMT2: | |
249 | sprintf (msgbuf, *msgp->format->fmt, | |
250 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, | |
251 | msgp->format->msg, sum, | |
252 | *msgp->format->type); | |
253 | break; | |
254 | } | |
255 | ||
256 | do_log(_("%s"), msgbuf); | |
257 | elapsed = now - phase_times[current_phase].start; | |
258 | if ((msgp->format->format == FMT1) && sum && elapsed && | |
259 | ((current_phase == 3) || | |
260 | (current_phase == 4) || | |
261 | (current_phase == 7))) { | |
262 | /* for inode phase report % complete */ | |
263 | do_log( | |
264 | _("\t- %02d:%02d:%02d: Phase %d: elapsed time %s - processed %d %s per minute\n"), | |
265 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, | |
266 | current_phase, duration(elapsed, msgbuf), | |
267 | (int) (60*sum/(elapsed)), *msgp->format->type); | |
268 | do_log( | |
5d1b7f0f | 269 | _("\t- %02d:%02d:%02d: Phase %d: %" PRIu64 "%% done - estimated remaining time %s\n"), |
06fbdda9 MV |
270 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, |
271 | current_phase, percent, | |
272 | duration((int) ((*msgp->total - sum) * (elapsed)/sum), msgbuf)); | |
273 | } | |
274 | ||
275 | if (pthread_mutex_unlock(&msgp->mutex) != 0) { | |
276 | do_error( | |
277 | _("progress_rpt: error unlock msg mutex\n")); | |
278 | } | |
279 | sigwait (&sigs_to_catch, &caught); | |
280 | } | |
281 | ||
282 | if (timer_delete (timerid)) | |
283 | do_warn(_("cannot delete timer\n")); | |
284 | ||
285 | free (msgbuf); | |
286 | return (NULL); | |
287 | } | |
288 | ||
289 | int | |
14f8b681 | 290 | set_progress_msg(int report, uint64_t total) |
06fbdda9 MV |
291 | { |
292 | ||
2556c98b | 293 | if (!ag_stride) |
06fbdda9 MV |
294 | return (0); |
295 | ||
296 | if (pthread_mutex_lock(&global_msgs.mutex)) | |
297 | do_error(_("set_progress_msg: cannot lock progress mutex\n")); | |
298 | ||
299 | prog_rpt_total = total; | |
300 | global_msgs.format = &progress_rpt_reports[report]; | |
301 | ||
302 | /* reset all the accumulative totals */ | |
303 | if (prog_rpt_done) | |
14f8b681 | 304 | bzero(prog_rpt_done, sizeof(uint64_t)*glob_agcount); |
06fbdda9 MV |
305 | |
306 | if (pthread_mutex_unlock(&global_msgs.mutex)) | |
307 | do_error(_("set_progress_msg: cannot unlock progress mutex\n")); | |
308 | ||
309 | return (0); | |
310 | } | |
311 | ||
14f8b681 | 312 | uint64_t |
06fbdda9 MV |
313 | print_final_rpt(void) |
314 | { | |
315 | int i; | |
316 | struct tm *tmp; | |
317 | time_t now; | |
14f8b681 DW |
318 | uint64_t *donep; |
319 | uint64_t sum; | |
06fbdda9 MV |
320 | msg_block_t *msgp = &global_msgs; |
321 | char msgbuf[DURATION_BUF_SIZE]; | |
2556c98b BN |
322 | |
323 | if (!ag_stride) | |
06fbdda9 MV |
324 | return 0; |
325 | ||
326 | if (pthread_mutex_lock(&global_msgs.mutex)) | |
327 | do_error(_("print_final_rpt: cannot lock progress mutex\n")); | |
328 | ||
329 | bzero(&msgbuf, sizeof(msgbuf)); | |
330 | ||
331 | now = time (NULL); | |
332 | tmp = localtime ((const time_t *) &now); | |
333 | ||
334 | /* | |
335 | * Sum the work | |
336 | */ | |
337 | ||
338 | sum = 0; | |
339 | donep = msgp->done; | |
340 | for (i = 0; i < msgp->count; i++) { | |
341 | sum += *donep++; | |
342 | } | |
343 | ||
344 | if (report_interval) { | |
345 | switch(msgp->format->format) { | |
346 | case FMT1: | |
fa42b46f | 347 | sprintf (msgbuf, _(*msgp->format->fmt), |
06fbdda9 | 348 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, |
fa42b46f JB |
349 | _(msgp->format->msg), sum, |
350 | *msgp->total, _(*msgp->format->type)); | |
06fbdda9 MV |
351 | break; |
352 | case FMT2: | |
fa42b46f | 353 | sprintf (msgbuf, _(*msgp->format->fmt), |
06fbdda9 | 354 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, |
fa42b46f JB |
355 | _(msgp->format->msg), sum, |
356 | _(*msgp->format->type)); | |
06fbdda9 MV |
357 | break; |
358 | } | |
359 | do_log(_("%s"), msgbuf); | |
360 | } | |
361 | ||
362 | if (pthread_mutex_unlock(&global_msgs.mutex)) | |
363 | do_error(_("print_final_rpt: cannot unlock progress mutex\n")); | |
364 | ||
365 | return(sum); | |
366 | } | |
367 | ||
8b8a6b02 | 368 | static void |
06fbdda9 MV |
369 | timediff(int phase) |
370 | { | |
371 | phase_times[phase].duration = | |
372 | phase_times[phase].end - phase_times[phase].start; | |
373 | ||
374 | } | |
375 | ||
376 | /* | |
377 | ** Get the time and save in the phase time | |
378 | ** array. | |
379 | */ | |
380 | char * | |
381 | timestamp(int end, int phase, char *buf) | |
382 | { | |
383 | ||
384 | time_t now; | |
385 | struct tm *tmp; | |
386 | ||
2556c98b BN |
387 | if (verbose > 1) |
388 | cache_report(stderr, "libxfs_bcache", libxfs_bcache); | |
389 | ||
06fbdda9 MV |
390 | now = time(NULL); |
391 | ||
392 | if (end) { | |
393 | phase_times[phase].end = now; | |
394 | timediff(phase); | |
395 | ||
396 | /* total time in slot zero */ | |
397 | phase_times[0].end = now; | |
398 | timediff(0); | |
399 | ||
400 | if (phase < 7) { | |
401 | phase_times[phase+1].start = now; | |
402 | current_phase = phase + 1; | |
403 | } | |
404 | } | |
405 | else { | |
406 | phase_times[phase].start = now; | |
407 | current_phase = phase; | |
408 | } | |
409 | ||
410 | if (buf) { | |
411 | tmp = localtime((const time_t *)&now); | |
412 | sprintf(buf, _("%02d:%02d:%02d"), tmp->tm_hour, tmp->tm_min, tmp->tm_sec); | |
413 | } | |
414 | return(buf); | |
415 | } | |
416 | ||
417 | char * | |
418 | duration(int length, char *buf) | |
419 | { | |
420 | int sum; | |
421 | int weeks; | |
422 | int days; | |
423 | int hours; | |
424 | int minutes; | |
425 | int seconds; | |
426 | char temp[128]; | |
427 | ||
428 | *buf = '\0'; | |
429 | weeks = days = hours = minutes = seconds = sum = 0; | |
430 | if (length >= ONEWEEK) { | |
431 | weeks = length / ONEWEEK; | |
432 | sum = (weeks * ONEWEEK); | |
433 | if (weeks) { | |
434 | sprintf(buf, _("%d week"), weeks); | |
435 | if (weeks > 1) strcat(buf, _("s")); | |
436 | if ((length-sum) == 0) | |
437 | return(buf); | |
438 | } | |
439 | } | |
440 | if (length >= ONEDAY) { | |
441 | days = (length - sum) / ONEDAY; | |
442 | sum += (days * ONEDAY); | |
443 | if (days) { | |
444 | sprintf(temp, _("%d day"), days); | |
445 | if (days > 1) strcat(temp, _("s")); | |
446 | if (((length-sum) == 0) && (!weeks)) { | |
447 | strcpy(buf, temp); | |
448 | return(buf); | |
449 | } | |
450 | else if (weeks) { | |
451 | strcat(buf, _(", ")); | |
452 | } | |
453 | strcat(buf, temp); | |
454 | } | |
455 | } | |
456 | if (length >= ONEHOUR) { | |
457 | hours = (length - sum) / ONEHOUR; | |
458 | sum += (hours * ONEHOUR); | |
459 | if (hours) { | |
460 | sprintf(temp, _("%d hour"), hours); | |
461 | if (hours > 1) strcat(temp, _("s")); | |
462 | if (((length-sum) == 0) && | |
463 | (!weeks) && (!days)) { | |
464 | strcpy(buf, temp); | |
465 | return(buf); | |
466 | } | |
467 | else if ((weeks) || (days)) { | |
468 | strcat(buf, _(", ")); | |
469 | } | |
470 | strcat(buf, temp); | |
471 | } | |
2556c98b | 472 | |
06fbdda9 MV |
473 | } |
474 | if (length >= ONEMINUTE) { | |
475 | minutes = (length - sum) / ONEMINUTE; | |
476 | sum += (minutes * ONEMINUTE); | |
477 | if (minutes) { | |
478 | sprintf(temp, _("%d minute"), minutes); | |
479 | if (minutes > 1) strcat(temp, _("s")); | |
480 | if (((length-sum) == 0) && | |
481 | (!weeks) && (!days) && (!hours)) { | |
482 | strcpy(buf, temp); | |
483 | return(buf); | |
484 | } | |
485 | else if ((weeks)||(days)||(hours)) { | |
486 | strcat(buf, _(", ")); | |
487 | } | |
488 | strcat(buf, temp); | |
489 | } | |
490 | } | |
491 | seconds = length - sum; | |
492 | if (seconds) { | |
493 | sprintf(temp, _("%d second"), seconds); | |
494 | if (seconds > 1) strcat(temp, _("s")); | |
495 | if ((weeks)||(days)||(hours)||(minutes)) | |
496 | strcat(buf, _(", ")); | |
497 | strcat(buf, temp); | |
498 | } | |
2556c98b | 499 | |
06fbdda9 MV |
500 | return(buf); |
501 | } | |
502 | ||
503 | void | |
504 | summary_report(void) | |
505 | { | |
506 | int i; | |
507 | time_t now; | |
508 | struct tm end; | |
509 | struct tm start; | |
510 | char msgbuf[DURATION_BUF_SIZE]; | |
511 | ||
512 | now = time(NULL); | |
513 | ||
514 | do_log(_("\n XFS_REPAIR Summary %s\n"), | |
515 | ctime((const time_t *)&now)); | |
516 | do_log(_("Phase\t\tStart\t\tEnd\t\tDuration\n")); | |
517 | for (i = 1; i < 8; i++) { | |
518 | localtime_r((const time_t *)&phase_times[i].start, &start); | |
519 | localtime_r((const time_t *)&phase_times[i].end, &end); | |
520 | if ((no_modify) && (i == 5)) { | |
521 | do_log(_("Phase %d:\tSkipped\n"), i); | |
522 | } | |
523 | else if ((bad_ino_btree) && ((i == 6) || (i == 7))) { | |
524 | do_log(_("Phase %d:\tSkipped\n"), i); | |
525 | } | |
526 | else { | |
527 | do_log( | |
528 | _("Phase %d:\t%02d/%02d %02d:%02d:%02d\t%02d/%02d %02d:%02d:%02d\t%s\n"), i, | |
529 | start.tm_mon+1, start.tm_mday, start.tm_hour, start.tm_min, start.tm_sec, | |
530 | end.tm_mon+1, end.tm_mday, end.tm_hour, end.tm_min, end.tm_sec, | |
531 | duration(phase_times[i].duration, msgbuf)); | |
532 | } | |
533 | } | |
534 | do_log(_("\nTotal run time: %s\n"), duration(phase_times[0].duration, msgbuf)); | |
535 | } |