]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - repair/progress.c
xfsprogs: make static things static
[thirdparty/xfsprogs-dev.git] / repair / progress.c
CommitLineData
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
14static
15char *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
35static
36char *rpt_fmts[] = {
37#define FMT1 0
97294b22 38N_(" - %02d:%02d:%02d: %s - %llu of %llu %s done\n"),
06fbdda9 39#define FMT2 1
97294b22 40N_(" - %02d:%02d:%02d: %s - %llu %s done\n"),
06fbdda9
MV
41};
42
43typedef struct progress_rpt_s {
44 short format;
45 char *msg;
46 char **fmt;
47 char **type;
48} progress_rpt_t;
49
50static
51progress_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 84static pthread_t report_thread;
06fbdda9
MV
85
86typedef 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;
94static msg_block_t global_msgs;
95
96typedef 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;
102static phase_times_t phase_times[8];
103
104static void *progress_rpt_thread(void *);
105static int current_phase;
106static int running;
14f8b681 107static uint64_t prog_rpt_total;
06fbdda9
MV
108
109void
110init_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
141void
142stop_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
157static void *
158progress_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(&timespec, 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, &timespec, 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
289int
14f8b681 290set_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 312uint64_t
06fbdda9
MV
313print_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 368static void
06fbdda9
MV
369timediff(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*/
380char *
381timestamp(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
417char *
418duration(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
503void
504summary_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}