]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - repair/progress.c
xfsprogs: use <>-style includes in installed headers
[thirdparty/xfsprogs-dev.git] / repair / progress.c
CommitLineData
06fbdda9 1
b08338d7 2#include "xfs/libxfs.h"
06fbdda9 3#include "globals.h"
2556c98b 4#include "progress.h"
06fbdda9
MV
5#include "err_protos.h"
6#include <signal.h>
7
8#define ONEMINUTE 60
9#define ONEHOUR (60*ONEMINUTE)
10#define ONEDAY (24*ONEHOUR)
11#define ONEWEEK (7*ONEDAY)
12
13static
14char *rpt_types[] = {
15#define TYPE_INODE 0
97294b22 16 N_("inodes"),
06fbdda9 17#define TYPE_BLOCK 1
97294b22 18 N_("blocks"),
06fbdda9 19#define TYPE_DIR 2
97294b22 20 N_("directories"),
06fbdda9 21#define TYPE_AG 3
97294b22 22 N_("allocation groups"),
06fbdda9 23#define TYPE_AGI_BUCKET 4
97294b22 24 N_("AGI unlinked buckets"),
06fbdda9 25#define TYPE_EXTENTS 5
97294b22 26 N_("extents"),
06fbdda9 27#define TYPE_RTEXTENTS 6
97294b22 28 N_("realtime extents"),
06fbdda9 29#define TYPE_UNLINKED_LIST 7
97294b22 30 N_("unlinked lists")
06fbdda9
MV
31};
32
33
34static
35char *rpt_fmts[] = {
36#define FMT1 0
97294b22 37N_(" - %02d:%02d:%02d: %s - %llu of %llu %s done\n"),
06fbdda9 38#define FMT2 1
97294b22 39N_(" - %02d:%02d:%02d: %s - %llu %s done\n"),
06fbdda9
MV
40};
41
42typedef struct progress_rpt_s {
43 short format;
44 char *msg;
45 char **fmt;
46 char **type;
47} progress_rpt_t;
48
49static
50progress_rpt_t progress_rpt_reports[] = {
97294b22 51{FMT1, N_("scanning filesystem freespace"), /* 0 */
06fbdda9 52 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
97294b22 53{FMT1, N_("scanning agi unlinked lists"), /* 1 */
06fbdda9 54 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
97294b22 55{FMT2, N_("check uncertain AG inodes"), /* 2 */
06fbdda9 56 &rpt_fmts[FMT2], &rpt_types[TYPE_AGI_BUCKET]},
97294b22 57{FMT1, N_("process known inodes and inode discovery"), /* 3 */
06fbdda9 58 &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]},
97294b22 59{FMT1, N_("process newly discovered inodes"), /* 4 */
06fbdda9 60 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
97294b22 61{FMT1, N_("setting up duplicate extent list"), /* 5 */
06fbdda9 62 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
97294b22 63{FMT1, N_("initialize realtime bitmap"), /* 6 */
06fbdda9 64 &rpt_fmts[FMT1], &rpt_types[TYPE_BLOCK]},
97294b22 65{FMT1, N_("reset realtime bitmaps"), /* 7 */
06fbdda9 66 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
97294b22 67{FMT1, N_("check for inodes claiming duplicate blocks"), /* 8 */
06fbdda9 68 &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]},
97294b22 69{FMT1, N_("rebuild AG headers and trees"), /* 9 */
06fbdda9 70 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
97294b22 71{FMT1, N_("traversing filesystem"), /* 10 */
06fbdda9 72 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
97294b22 73{FMT2, N_("traversing all unattached subtrees"), /* 11 */
06fbdda9 74 &rpt_fmts[FMT2], &rpt_types[TYPE_DIR]},
97294b22 75{FMT2, N_("moving disconnected inodes to lost+found"), /* 12 */
06fbdda9 76 &rpt_fmts[FMT2], &rpt_types[TYPE_INODE]},
97294b22 77{FMT1, N_("verify and correct link counts"), /* 13 */
06fbdda9 78 &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]},
97294b22 79{FMT1, N_("verify link counts"), /* 14 */
06fbdda9
MV
80 &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]}
81};
82
83pthread_t report_thread;
84
85typedef struct msg_block_s {
86 pthread_mutex_t mutex;
87 progress_rpt_t *format;
88 __uint64_t *done;
89 __uint64_t *total;
90 int count;
91 int interval;
92} msg_block_t;
93static msg_block_t global_msgs;
94
95typedef struct phase_times_s {
96 time_t start;
97 time_t end;
98 time_t duration;
2556c98b 99 __uint64_t item_counts[4];
06fbdda9
MV
100} phase_times_t;
101static phase_times_t phase_times[8];
102
103static void *progress_rpt_thread(void *);
104static int current_phase;
105static int running;
106static __uint64_t prog_rpt_total;
107
108void
109init_progress_rpt (void)
110{
111
112 /*
113 * allocate the done vector
114 */
115
116 if ((prog_rpt_done = (__uint64_t *)
117 malloc(sizeof(__uint64_t)*glob_agcount)) == NULL ) {
118 do_error(_("cannot malloc pointer to done vector\n"));
119 }
120 bzero(prog_rpt_done, sizeof(__uint64_t)*glob_agcount);
121
122 /*
123 * Setup comm block, start the thread
124 */
125
126 pthread_mutex_init(&global_msgs.mutex, NULL);
7f2d6b81 127 global_msgs.format = NULL;
06fbdda9
MV
128 global_msgs.count = glob_agcount;
129 global_msgs.interval = report_interval;
130 global_msgs.done = prog_rpt_done;
131 global_msgs.total = &prog_rpt_total;
132
133 if (pthread_create (&report_thread, NULL,
134 progress_rpt_thread, (void *)&global_msgs))
135 do_error(_("unable to create progress report thread\n"));
136
137 return;
138}
139
140void
141stop_progress_rpt(void)
142{
143
144 /*
145 * Tell msg thread to shutdown,
146 * wait for all threads to finished
147 */
148
149 running = 0;
150 pthread_kill (report_thread, SIGHUP);
151 pthread_join (report_thread, NULL);
152 free(prog_rpt_done);
153 return;
154}
155
156static void *
157progress_rpt_thread (void *p)
158{
159
160 int i;
161 int caught;
162 sigset_t sigs_to_catch;
163 struct tm *tmp;
164 time_t now, elapsed;
165 timer_t timerid;
166 struct itimerspec timespec;
167 char *msgbuf;
168 __uint64_t *donep;
169 __uint64_t sum;
170 msg_block_t *msgp = (msg_block_t *)p;
171 __uint64_t percent;
172
7f2d6b81
ES
173 /* It's possible to get here very early w/ no progress msg set */
174 if (!msgp->format)
175 return NULL;
176
06fbdda9
MV
177 if ((msgbuf = (char *)malloc(DURATION_BUF_SIZE)) == NULL)
178 do_error (_("progress_rpt: cannot malloc progress msg buffer\n"));
179
180 running = 1;
181
182 /*
183 * Specify a repeating timer that fires each MSG_INTERVAL seconds.
184 */
2556c98b 185
06fbdda9
MV
186 timespec.it_value.tv_sec = msgp->interval;
187 timespec.it_value.tv_nsec = 0;
188 timespec.it_interval.tv_sec = msgp->interval;
189 timespec.it_interval.tv_nsec = 0;
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
290set_progress_msg (int report, __uint64_t total)
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)
304 bzero(prog_rpt_done, sizeof(__uint64_t)*glob_agcount);
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
312__uint64_t
313print_final_rpt(void)
314{
315 int i;
316 struct tm *tmp;
317 time_t now;
318 __uint64_t *donep;
319 __uint64_t sum;
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}