]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - repair/progress.c
libxfs: refactor manage_zones()
[thirdparty/xfsprogs-dev.git] / repair / progress.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include "libxfs.h"
4 #include "globals.h"
5 #include "progress.h"
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
17 N_("inodes"),
18 #define TYPE_BLOCK 1
19 N_("blocks"),
20 #define TYPE_DIR 2
21 N_("directories"),
22 #define TYPE_AG 3
23 N_("allocation groups"),
24 #define TYPE_AGI_BUCKET 4
25 N_("AGI unlinked buckets"),
26 #define TYPE_EXTENTS 5
27 N_("extents"),
28 #define TYPE_RTEXTENTS 6
29 N_("realtime extents"),
30 #define TYPE_UNLINKED_LIST 7
31 N_("unlinked lists")
32 };
33
34
35 static
36 char *rpt_fmts[] = {
37 #define FMT1 0
38 N_(" - %02d:%02d:%02d: %s - %llu of %llu %s done\n"),
39 #define FMT2 1
40 N_(" - %02d:%02d:%02d: %s - %llu %s done\n"),
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[] = {
52 {FMT1, N_("scanning filesystem freespace"), /* 0 */
53 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
54 {FMT1, N_("scanning agi unlinked lists"), /* 1 */
55 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
56 {FMT2, N_("check uncertain AG inodes"), /* 2 */
57 &rpt_fmts[FMT2], &rpt_types[TYPE_AGI_BUCKET]},
58 {FMT1, N_("process known inodes and inode discovery"), /* 3 */
59 &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]},
60 {FMT1, N_("process newly discovered inodes"), /* 4 */
61 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
62 {FMT1, N_("setting up duplicate extent list"), /* 5 */
63 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
64 {FMT1, N_("initialize realtime bitmap"), /* 6 */
65 &rpt_fmts[FMT1], &rpt_types[TYPE_BLOCK]},
66 {FMT1, N_("reset realtime bitmaps"), /* 7 */
67 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
68 {FMT1, N_("check for inodes claiming duplicate blocks"), /* 8 */
69 &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]},
70 {FMT1, N_("rebuild AG headers and trees"), /* 9 */
71 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
72 {FMT1, N_("traversing filesystem"), /* 10 */
73 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
74 {FMT2, N_("traversing all unattached subtrees"), /* 11 */
75 &rpt_fmts[FMT2], &rpt_types[TYPE_DIR]},
76 {FMT2, N_("moving disconnected inodes to lost+found"), /* 12 */
77 &rpt_fmts[FMT2], &rpt_types[TYPE_INODE]},
78 {FMT1, N_("verify and correct link counts"), /* 13 */
79 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
80 {FMT1, N_("verify link counts"), /* 14 */
81 &rpt_fmts[FMT1], &rpt_types[TYPE_AG]}
82 };
83
84 static pthread_t report_thread;
85
86 typedef struct msg_block_s {
87 pthread_mutex_t mutex;
88 progress_rpt_t *format;
89 uint64_t *done;
90 uint64_t *total;
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;
100 uint64_t item_counts[4];
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;
107 static uint64_t prog_rpt_total;
108
109 void
110 init_progress_rpt (void)
111 {
112
113 /*
114 * allocate the done vector
115 */
116
117 if ((prog_rpt_done = (uint64_t *)
118 malloc(sizeof(uint64_t)*glob_agcount)) == NULL) {
119 do_error(_("cannot malloc pointer to done vector\n"));
120 }
121 bzero(prog_rpt_done, sizeof(uint64_t)*glob_agcount);
122
123 /*
124 * Setup comm block, start the thread
125 */
126
127 pthread_mutex_init(&global_msgs.mutex, NULL);
128 global_msgs.format = NULL;
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;
169 uint64_t *donep;
170 uint64_t sum;
171 msg_block_t *msgp = (msg_block_t *)p;
172 uint64_t percent;
173
174 /* It's possible to get here very early w/ no progress msg set */
175 if (!msgp->format)
176 return NULL;
177
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 */
186
187 memset(&timespec, 0, sizeof(timespec));
188 timespec.it_value.tv_sec = msgp->interval;
189 timespec.it_interval.tv_sec = msgp->interval;
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(
269 _("\t- %02d:%02d:%02d: Phase %d: %" PRIu64 "%% done - estimated remaining time %s\n"),
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
290 set_progress_msg(int report, uint64_t total)
291 {
292
293 if (!ag_stride)
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
313 print_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];
322
323 if (!ag_stride)
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:
347 sprintf (msgbuf, _(*msgp->format->fmt),
348 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
349 _(msgp->format->msg), sum,
350 *msgp->total, _(*msgp->format->type));
351 break;
352 case FMT2:
353 sprintf (msgbuf, _(*msgp->format->fmt),
354 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
355 _(msgp->format->msg), sum,
356 _(*msgp->format->type));
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
368 static void
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
387 if (verbose > 1)
388 cache_report(stderr, "libxfs_bcache", libxfs_bcache);
389
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 }
472
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 }
499
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 }