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