]>
Commit | Line | Data |
---|---|---|
06fbdda9 MV |
1 | |
2 | #include <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 | ||
13 | static | |
14 | char *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 | ||
34 | static | |
35 | char *rpt_fmts[] = { | |
36 | #define FMT1 0 | |
97294b22 | 37 | N_(" - %02d:%02d:%02d: %s - %llu of %llu %s done\n"), |
06fbdda9 | 38 | #define FMT2 1 |
97294b22 | 39 | N_(" - %02d:%02d:%02d: %s - %llu %s done\n"), |
06fbdda9 MV |
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[] = { | |
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 | ||
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; | |
2556c98b | 99 | __uint64_t item_counts[4]; |
06fbdda9 MV |
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.count = glob_agcount; | |
128 | global_msgs.interval = report_interval; | |
129 | global_msgs.done = prog_rpt_done; | |
130 | global_msgs.total = &prog_rpt_total; | |
131 | ||
132 | if (pthread_create (&report_thread, NULL, | |
133 | progress_rpt_thread, (void *)&global_msgs)) | |
134 | do_error(_("unable to create progress report thread\n")); | |
135 | ||
136 | return; | |
137 | } | |
138 | ||
139 | void | |
140 | stop_progress_rpt(void) | |
141 | { | |
142 | ||
143 | /* | |
144 | * Tell msg thread to shutdown, | |
145 | * wait for all threads to finished | |
146 | */ | |
147 | ||
148 | running = 0; | |
149 | pthread_kill (report_thread, SIGHUP); | |
150 | pthread_join (report_thread, NULL); | |
151 | free(prog_rpt_done); | |
152 | return; | |
153 | } | |
154 | ||
155 | static void * | |
156 | progress_rpt_thread (void *p) | |
157 | { | |
158 | ||
159 | int i; | |
160 | int caught; | |
161 | sigset_t sigs_to_catch; | |
162 | struct tm *tmp; | |
163 | time_t now, elapsed; | |
164 | timer_t timerid; | |
165 | struct itimerspec timespec; | |
166 | char *msgbuf; | |
167 | __uint64_t *donep; | |
168 | __uint64_t sum; | |
169 | msg_block_t *msgp = (msg_block_t *)p; | |
170 | __uint64_t percent; | |
171 | ||
172 | if ((msgbuf = (char *)malloc(DURATION_BUF_SIZE)) == NULL) | |
173 | do_error (_("progress_rpt: cannot malloc progress msg buffer\n")); | |
174 | ||
175 | running = 1; | |
176 | ||
177 | /* | |
178 | * Specify a repeating timer that fires each MSG_INTERVAL seconds. | |
179 | */ | |
2556c98b | 180 | |
06fbdda9 MV |
181 | timespec.it_value.tv_sec = msgp->interval; |
182 | timespec.it_value.tv_nsec = 0; | |
183 | timespec.it_interval.tv_sec = msgp->interval; | |
184 | timespec.it_interval.tv_nsec = 0; | |
185 | ||
186 | if (timer_create (CLOCK_REALTIME, NULL, &timerid)) | |
187 | do_error(_("progress_rpt: cannot create timer\n")); | |
188 | ||
189 | if (timer_settime (timerid, 0, ×pec, NULL)) | |
190 | do_error(_("progress_rpt: cannot set timer\n")); | |
191 | ||
192 | /* | |
193 | * Main loop - output messages based on periodic signal arrival | |
194 | * set this thread's signal mask to block out all other signals | |
195 | */ | |
196 | ||
197 | sigemptyset (&sigs_to_catch); | |
198 | sigaddset (&sigs_to_catch, SIGALRM); | |
199 | sigaddset (&sigs_to_catch, SIGHUP); | |
200 | sigwait (&sigs_to_catch, &caught); | |
201 | ||
202 | while (caught != SIGHUP) { | |
203 | /* | |
204 | * Allow the mainline to hold off messages by holding | |
205 | * the lock. We don't want to just skip a period in case the | |
206 | * reporting interval is very long... people get nervous. But, | |
207 | * if the interval is very short, we can't let the timer go | |
208 | * off again without sigwait'ing for it. So disarm the timer | |
209 | * while we try to get the lock and giveup the cpu... the | |
210 | * mainline shouldn't take that long. | |
211 | */ | |
212 | ||
213 | if (pthread_mutex_lock(&msgp->mutex)) { | |
214 | do_error(_("progress_rpt: cannot lock progress mutex\n")); | |
215 | } | |
216 | ||
217 | if (!running) | |
218 | break; | |
219 | ||
220 | now = time (NULL); | |
221 | tmp = localtime ((const time_t *) &now); | |
222 | ||
223 | /* | |
224 | * Sum the work | |
225 | */ | |
226 | ||
227 | sum = 0; | |
228 | donep = msgp->done; | |
229 | for (i = 0; i < msgp->count; i++) { | |
230 | sum += *donep++; | |
231 | } | |
232 | ||
233 | percent = 0; | |
234 | switch(msgp->format->format) { | |
235 | case FMT1: | |
236 | if (*msgp->total) | |
237 | percent = (sum * 100) / ( *msgp->total ); | |
238 | sprintf (msgbuf, *msgp->format->fmt, | |
239 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, | |
240 | msgp->format->msg, sum, | |
241 | *msgp->total, *msgp->format->type); | |
242 | break; | |
243 | case FMT2: | |
244 | sprintf (msgbuf, *msgp->format->fmt, | |
245 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, | |
246 | msgp->format->msg, sum, | |
247 | *msgp->format->type); | |
248 | break; | |
249 | } | |
250 | ||
251 | do_log(_("%s"), msgbuf); | |
252 | elapsed = now - phase_times[current_phase].start; | |
253 | if ((msgp->format->format == FMT1) && sum && elapsed && | |
254 | ((current_phase == 3) || | |
255 | (current_phase == 4) || | |
256 | (current_phase == 7))) { | |
257 | /* for inode phase report % complete */ | |
258 | do_log( | |
259 | _("\t- %02d:%02d:%02d: Phase %d: elapsed time %s - processed %d %s per minute\n"), | |
260 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, | |
261 | current_phase, duration(elapsed, msgbuf), | |
262 | (int) (60*sum/(elapsed)), *msgp->format->type); | |
263 | do_log( | |
264 | _("\t- %02d:%02d:%02d: Phase %d: %llu%% done - estimated remaining time %s\n"), | |
265 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, | |
266 | current_phase, percent, | |
267 | duration((int) ((*msgp->total - sum) * (elapsed)/sum), msgbuf)); | |
268 | } | |
269 | ||
270 | if (pthread_mutex_unlock(&msgp->mutex) != 0) { | |
271 | do_error( | |
272 | _("progress_rpt: error unlock msg mutex\n")); | |
273 | } | |
274 | sigwait (&sigs_to_catch, &caught); | |
275 | } | |
276 | ||
277 | if (timer_delete (timerid)) | |
278 | do_warn(_("cannot delete timer\n")); | |
279 | ||
280 | free (msgbuf); | |
281 | return (NULL); | |
282 | } | |
283 | ||
284 | int | |
285 | set_progress_msg (int report, __uint64_t total) | |
286 | { | |
287 | ||
2556c98b | 288 | if (!ag_stride) |
06fbdda9 MV |
289 | return (0); |
290 | ||
291 | if (pthread_mutex_lock(&global_msgs.mutex)) | |
292 | do_error(_("set_progress_msg: cannot lock progress mutex\n")); | |
293 | ||
294 | prog_rpt_total = total; | |
295 | global_msgs.format = &progress_rpt_reports[report]; | |
296 | ||
297 | /* reset all the accumulative totals */ | |
298 | if (prog_rpt_done) | |
299 | bzero(prog_rpt_done, sizeof(__uint64_t)*glob_agcount); | |
300 | ||
301 | if (pthread_mutex_unlock(&global_msgs.mutex)) | |
302 | do_error(_("set_progress_msg: cannot unlock progress mutex\n")); | |
303 | ||
304 | return (0); | |
305 | } | |
306 | ||
307 | __uint64_t | |
308 | print_final_rpt(void) | |
309 | { | |
310 | int i; | |
311 | struct tm *tmp; | |
312 | time_t now; | |
313 | __uint64_t *donep; | |
314 | __uint64_t sum; | |
315 | msg_block_t *msgp = &global_msgs; | |
316 | char msgbuf[DURATION_BUF_SIZE]; | |
2556c98b BN |
317 | |
318 | if (!ag_stride) | |
06fbdda9 MV |
319 | return 0; |
320 | ||
321 | if (pthread_mutex_lock(&global_msgs.mutex)) | |
322 | do_error(_("print_final_rpt: cannot lock progress mutex\n")); | |
323 | ||
324 | bzero(&msgbuf, sizeof(msgbuf)); | |
325 | ||
326 | now = time (NULL); | |
327 | tmp = localtime ((const time_t *) &now); | |
328 | ||
329 | /* | |
330 | * Sum the work | |
331 | */ | |
332 | ||
333 | sum = 0; | |
334 | donep = msgp->done; | |
335 | for (i = 0; i < msgp->count; i++) { | |
336 | sum += *donep++; | |
337 | } | |
338 | ||
339 | if (report_interval) { | |
340 | switch(msgp->format->format) { | |
341 | case FMT1: | |
342 | sprintf (msgbuf, *msgp->format->fmt, | |
343 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, | |
344 | msgp->format->msg, sum, | |
345 | *msgp->total, *msgp->format->type); | |
346 | break; | |
347 | case FMT2: | |
348 | sprintf (msgbuf, *msgp->format->fmt, | |
349 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, | |
350 | msgp->format->msg, sum, | |
351 | *msgp->format->type); | |
352 | break; | |
353 | } | |
354 | do_log(_("%s"), msgbuf); | |
355 | } | |
356 | ||
357 | if (pthread_mutex_unlock(&global_msgs.mutex)) | |
358 | do_error(_("print_final_rpt: cannot unlock progress mutex\n")); | |
359 | ||
360 | return(sum); | |
361 | } | |
362 | ||
363 | void | |
364 | timediff(int phase) | |
365 | { | |
366 | phase_times[phase].duration = | |
367 | phase_times[phase].end - phase_times[phase].start; | |
368 | ||
369 | } | |
370 | ||
371 | /* | |
372 | ** Get the time and save in the phase time | |
373 | ** array. | |
374 | */ | |
375 | char * | |
376 | timestamp(int end, int phase, char *buf) | |
377 | { | |
378 | ||
379 | time_t now; | |
380 | struct tm *tmp; | |
381 | ||
2556c98b BN |
382 | if (verbose > 1) |
383 | cache_report(stderr, "libxfs_bcache", libxfs_bcache); | |
384 | ||
06fbdda9 MV |
385 | now = time(NULL); |
386 | ||
387 | if (end) { | |
388 | phase_times[phase].end = now; | |
389 | timediff(phase); | |
390 | ||
391 | /* total time in slot zero */ | |
392 | phase_times[0].end = now; | |
393 | timediff(0); | |
394 | ||
395 | if (phase < 7) { | |
396 | phase_times[phase+1].start = now; | |
397 | current_phase = phase + 1; | |
398 | } | |
399 | } | |
400 | else { | |
401 | phase_times[phase].start = now; | |
402 | current_phase = phase; | |
403 | } | |
404 | ||
405 | if (buf) { | |
406 | tmp = localtime((const time_t *)&now); | |
407 | sprintf(buf, _("%02d:%02d:%02d"), tmp->tm_hour, tmp->tm_min, tmp->tm_sec); | |
408 | } | |
409 | return(buf); | |
410 | } | |
411 | ||
412 | char * | |
413 | duration(int length, char *buf) | |
414 | { | |
415 | int sum; | |
416 | int weeks; | |
417 | int days; | |
418 | int hours; | |
419 | int minutes; | |
420 | int seconds; | |
421 | char temp[128]; | |
422 | ||
423 | *buf = '\0'; | |
424 | weeks = days = hours = minutes = seconds = sum = 0; | |
425 | if (length >= ONEWEEK) { | |
426 | weeks = length / ONEWEEK; | |
427 | sum = (weeks * ONEWEEK); | |
428 | if (weeks) { | |
429 | sprintf(buf, _("%d week"), weeks); | |
430 | if (weeks > 1) strcat(buf, _("s")); | |
431 | if ((length-sum) == 0) | |
432 | return(buf); | |
433 | } | |
434 | } | |
435 | if (length >= ONEDAY) { | |
436 | days = (length - sum) / ONEDAY; | |
437 | sum += (days * ONEDAY); | |
438 | if (days) { | |
439 | sprintf(temp, _("%d day"), days); | |
440 | if (days > 1) strcat(temp, _("s")); | |
441 | if (((length-sum) == 0) && (!weeks)) { | |
442 | strcpy(buf, temp); | |
443 | return(buf); | |
444 | } | |
445 | else if (weeks) { | |
446 | strcat(buf, _(", ")); | |
447 | } | |
448 | strcat(buf, temp); | |
449 | } | |
450 | } | |
451 | if (length >= ONEHOUR) { | |
452 | hours = (length - sum) / ONEHOUR; | |
453 | sum += (hours * ONEHOUR); | |
454 | if (hours) { | |
455 | sprintf(temp, _("%d hour"), hours); | |
456 | if (hours > 1) strcat(temp, _("s")); | |
457 | if (((length-sum) == 0) && | |
458 | (!weeks) && (!days)) { | |
459 | strcpy(buf, temp); | |
460 | return(buf); | |
461 | } | |
462 | else if ((weeks) || (days)) { | |
463 | strcat(buf, _(", ")); | |
464 | } | |
465 | strcat(buf, temp); | |
466 | } | |
2556c98b | 467 | |
06fbdda9 MV |
468 | } |
469 | if (length >= ONEMINUTE) { | |
470 | minutes = (length - sum) / ONEMINUTE; | |
471 | sum += (minutes * ONEMINUTE); | |
472 | if (minutes) { | |
473 | sprintf(temp, _("%d minute"), minutes); | |
474 | if (minutes > 1) strcat(temp, _("s")); | |
475 | if (((length-sum) == 0) && | |
476 | (!weeks) && (!days) && (!hours)) { | |
477 | strcpy(buf, temp); | |
478 | return(buf); | |
479 | } | |
480 | else if ((weeks)||(days)||(hours)) { | |
481 | strcat(buf, _(", ")); | |
482 | } | |
483 | strcat(buf, temp); | |
484 | } | |
485 | } | |
486 | seconds = length - sum; | |
487 | if (seconds) { | |
488 | sprintf(temp, _("%d second"), seconds); | |
489 | if (seconds > 1) strcat(temp, _("s")); | |
490 | if ((weeks)||(days)||(hours)||(minutes)) | |
491 | strcat(buf, _(", ")); | |
492 | strcat(buf, temp); | |
493 | } | |
2556c98b | 494 | |
06fbdda9 MV |
495 | return(buf); |
496 | } | |
497 | ||
498 | void | |
499 | summary_report(void) | |
500 | { | |
501 | int i; | |
502 | time_t now; | |
503 | struct tm end; | |
504 | struct tm start; | |
505 | char msgbuf[DURATION_BUF_SIZE]; | |
506 | ||
507 | now = time(NULL); | |
508 | ||
509 | do_log(_("\n XFS_REPAIR Summary %s\n"), | |
510 | ctime((const time_t *)&now)); | |
511 | do_log(_("Phase\t\tStart\t\tEnd\t\tDuration\n")); | |
512 | for (i = 1; i < 8; i++) { | |
513 | localtime_r((const time_t *)&phase_times[i].start, &start); | |
514 | localtime_r((const time_t *)&phase_times[i].end, &end); | |
515 | if ((no_modify) && (i == 5)) { | |
516 | do_log(_("Phase %d:\tSkipped\n"), i); | |
517 | } | |
518 | else if ((bad_ino_btree) && ((i == 6) || (i == 7))) { | |
519 | do_log(_("Phase %d:\tSkipped\n"), i); | |
520 | } | |
521 | else { | |
522 | do_log( | |
523 | _("Phase %d:\t%02d/%02d %02d:%02d:%02d\t%02d/%02d %02d:%02d:%02d\t%s\n"), i, | |
524 | start.tm_mon+1, start.tm_mday, start.tm_hour, start.tm_min, start.tm_sec, | |
525 | end.tm_mon+1, end.tm_mday, end.tm_hour, end.tm_min, end.tm_sec, | |
526 | duration(phase_times[i].duration, msgbuf)); | |
527 | } | |
528 | } | |
529 | do_log(_("\nTotal run time: %s\n"), duration(phase_times[0].duration, msgbuf)); | |
530 | } |