1 // SPDX-License-Identifier: GPL-2.0
6 #include "err_protos.h"
10 #define ONEHOUR (60*ONEMINUTE)
11 #define ONEDAY (24*ONEHOUR)
12 #define ONEWEEK (7*ONEDAY)
23 N_("allocation groups"),
24 #define TYPE_AGI_BUCKET 4
25 N_("AGI unlinked buckets"),
26 #define TYPE_EXTENTS 5
28 #define TYPE_RTEXTENTS 6
29 N_("realtime extents"),
30 #define TYPE_UNLINKED_LIST 7
38 N_(" - %02d:%02d:%02d: %s - %llu of %llu %s done\n"),
40 N_(" - %02d:%02d:%02d: %s - %llu %s done\n"),
43 typedef struct progress_rpt_s
{
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
]}
84 static pthread_t report_thread
;
86 typedef struct msg_block_s
{
87 pthread_mutex_t mutex
;
88 progress_rpt_t
*format
;
94 static msg_block_t global_msgs
;
96 typedef struct phase_times_s
{
100 uint64_t item_counts
[4];
102 static phase_times_t phase_times
[8];
104 static void *progress_rpt_thread(void *);
105 static int current_phase
;
107 static uint64_t prog_rpt_total
;
110 init_progress_rpt (void)
114 * allocate the done vector
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"));
121 bzero(prog_rpt_done
, sizeof(uint64_t)*glob_agcount
);
124 * Setup comm block, start the thread
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
;
134 if (pthread_create (&report_thread
, NULL
,
135 progress_rpt_thread
, (void *)&global_msgs
))
136 do_error(_("unable to create progress report thread\n"));
142 stop_progress_rpt(void)
146 * Tell msg thread to shutdown,
147 * wait for all threads to finished
151 pthread_kill (report_thread
, SIGHUP
);
152 pthread_join (report_thread
, NULL
);
158 progress_rpt_thread (void *p
)
163 sigset_t sigs_to_catch
;
167 struct itimerspec timespec
;
171 msg_block_t
*msgp
= (msg_block_t
*)p
;
174 /* It's possible to get here very early w/ no progress msg set */
178 if ((msgbuf
= (char *)malloc(DURATION_BUF_SIZE
)) == NULL
)
179 do_error (_("progress_rpt: cannot malloc progress msg buffer\n"));
184 * Specify a repeating timer that fires each MSG_INTERVAL seconds.
187 memset(×pec
, 0, sizeof(timespec
));
188 timespec
.it_value
.tv_sec
= msgp
->interval
;
189 timespec
.it_interval
.tv_sec
= msgp
->interval
;
191 if (timer_create (CLOCK_REALTIME
, NULL
, &timerid
))
192 do_error(_("progress_rpt: cannot create timer\n"));
194 if (timer_settime (timerid
, 0, ×pec
, NULL
))
195 do_error(_("progress_rpt: cannot set timer\n"));
198 * Main loop - output messages based on periodic signal arrival
199 * set this thread's signal mask to block out all other signals
202 sigemptyset (&sigs_to_catch
);
203 sigaddset (&sigs_to_catch
, SIGALRM
);
204 sigaddset (&sigs_to_catch
, SIGHUP
);
205 sigwait (&sigs_to_catch
, &caught
);
207 while (caught
!= SIGHUP
) {
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.
218 if (pthread_mutex_lock(&msgp
->mutex
)) {
219 do_error(_("progress_rpt: cannot lock progress mutex\n"));
226 tmp
= localtime ((const time_t *) &now
);
234 for (i
= 0; i
< msgp
->count
; i
++) {
239 switch(msgp
->format
->format
) {
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
);
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
);
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 */
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
);
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
));
275 if (pthread_mutex_unlock(&msgp
->mutex
) != 0) {
277 _("progress_rpt: error unlock msg mutex\n"));
279 sigwait (&sigs_to_catch
, &caught
);
282 if (timer_delete (timerid
))
283 do_warn(_("cannot delete timer\n"));
290 set_progress_msg(int report
, uint64_t total
)
296 if (pthread_mutex_lock(&global_msgs
.mutex
))
297 do_error(_("set_progress_msg: cannot lock progress mutex\n"));
299 prog_rpt_total
= total
;
300 global_msgs
.format
= &progress_rpt_reports
[report
];
302 /* reset all the accumulative totals */
304 bzero(prog_rpt_done
, sizeof(uint64_t)*glob_agcount
);
306 if (pthread_mutex_unlock(&global_msgs
.mutex
))
307 do_error(_("set_progress_msg: cannot unlock progress mutex\n"));
313 print_final_rpt(void)
320 msg_block_t
*msgp
= &global_msgs
;
321 char msgbuf
[DURATION_BUF_SIZE
];
326 if (pthread_mutex_lock(&global_msgs
.mutex
))
327 do_error(_("print_final_rpt: cannot lock progress mutex\n"));
329 bzero(&msgbuf
, sizeof(msgbuf
));
332 tmp
= localtime ((const time_t *) &now
);
340 for (i
= 0; i
< msgp
->count
; i
++) {
344 if (report_interval
) {
345 switch(msgp
->format
->format
) {
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
));
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
));
359 do_log(_("%s"), msgbuf
);
362 if (pthread_mutex_unlock(&global_msgs
.mutex
))
363 do_error(_("print_final_rpt: cannot unlock progress mutex\n"));
371 phase_times
[phase
].duration
=
372 phase_times
[phase
].end
- phase_times
[phase
].start
;
377 ** Get the time and save in the phase time
381 timestamp(int end
, int phase
, char *buf
)
388 cache_report(stderr
, "libxfs_bcache", libxfs_bcache
);
393 phase_times
[phase
].end
= now
;
396 /* total time in slot zero */
397 phase_times
[0].end
= now
;
401 phase_times
[phase
+1].start
= now
;
402 current_phase
= phase
+ 1;
406 phase_times
[phase
].start
= now
;
407 current_phase
= phase
;
411 tmp
= localtime((const time_t *)&now
);
412 sprintf(buf
, _("%02d:%02d:%02d"), tmp
->tm_hour
, tmp
->tm_min
, tmp
->tm_sec
);
418 duration(int length
, char *buf
)
429 weeks
= days
= hours
= minutes
= seconds
= sum
= 0;
430 if (length
>= ONEWEEK
) {
431 weeks
= length
/ ONEWEEK
;
432 sum
= (weeks
* ONEWEEK
);
434 sprintf(buf
, _("%d week"), weeks
);
435 if (weeks
> 1) strcat(buf
, _("s"));
436 if ((length
-sum
) == 0)
440 if (length
>= ONEDAY
) {
441 days
= (length
- sum
) / ONEDAY
;
442 sum
+= (days
* ONEDAY
);
444 sprintf(temp
, _("%d day"), days
);
445 if (days
> 1) strcat(temp
, _("s"));
446 if (((length
-sum
) == 0) && (!weeks
)) {
451 strcat(buf
, _(", "));
456 if (length
>= ONEHOUR
) {
457 hours
= (length
- sum
) / ONEHOUR
;
458 sum
+= (hours
* ONEHOUR
);
460 sprintf(temp
, _("%d hour"), hours
);
461 if (hours
> 1) strcat(temp
, _("s"));
462 if (((length
-sum
) == 0) &&
463 (!weeks
) && (!days
)) {
467 else if ((weeks
) || (days
)) {
468 strcat(buf
, _(", "));
474 if (length
>= ONEMINUTE
) {
475 minutes
= (length
- sum
) / ONEMINUTE
;
476 sum
+= (minutes
* ONEMINUTE
);
478 sprintf(temp
, _("%d minute"), minutes
);
479 if (minutes
> 1) strcat(temp
, _("s"));
480 if (((length
-sum
) == 0) &&
481 (!weeks
) && (!days
) && (!hours
)) {
485 else if ((weeks
)||(days
)||(hours
)) {
486 strcat(buf
, _(", "));
491 seconds
= length
- sum
;
493 sprintf(temp
, _("%d second"), seconds
);
494 if (seconds
> 1) strcat(temp
, _("s"));
495 if ((weeks
)||(days
)||(hours
)||(minutes
))
496 strcat(buf
, _(", "));
510 char msgbuf
[DURATION_BUF_SIZE
];
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
);
523 else if ((bad_ino_btree
) && ((i
== 6) || (i
== 7))) {
524 do_log(_("Phase %d:\tSkipped\n"), i
);
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
));
534 do_log(_("\nTotal run time: %s\n"), duration(phase_times
[0].duration
, msgbuf
));