--- /dev/null
+
+#include <libxfs.h>
+#include "progress.h"
+#include "globals.h"
+#include "err_protos.h"
+#include <signal.h>
+
+#define ONEMINUTE 60
+#define ONEHOUR (60*ONEMINUTE)
+#define ONEDAY (24*ONEHOUR)
+#define ONEWEEK (7*ONEDAY)
+
+static
+char *rpt_types[] = {
+#define TYPE_INODE 0
+ _("inodes"),
+#define TYPE_BLOCK 1
+ _("blocks"),
+#define TYPE_DIR 2
+ _("directories"),
+#define TYPE_AG 3
+ _("allocation groups"),
+#define TYPE_AGI_BUCKET 4
+ _("AGI unlinked buckets"),
+#define TYPE_EXTENTS 5
+ _("extents"),
+#define TYPE_RTEXTENTS 6
+ _("realtime extents"),
+#define TYPE_UNLINKED_LIST 7
+ _("unlinked lists")
+};
+
+
+static
+char *rpt_fmts[] = {
+#define FMT1 0
+_(" - %02d:%02d:%02d: %s - %llu of %llu %s done\n"),
+#define FMT2 1
+_(" - %02d:%02d:%02d: %s - %llu %s done\n"),
+};
+
+typedef struct progress_rpt_s {
+ short format;
+ char *msg;
+ char **fmt;
+ char **type;
+} progress_rpt_t;
+
+static
+progress_rpt_t progress_rpt_reports[] = {
+{FMT1, _("scanning filesystem freespace"), /* 0 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
+{FMT1, _("scanning agi unlinked lists"), /* 1 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
+{FMT2, _("check uncertain AG inodes"), /* 2 */
+ &rpt_fmts[FMT2], &rpt_types[TYPE_AGI_BUCKET]},
+{FMT1, _("process known inodes and inode discovery"), /* 3 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]},
+{FMT1, _("process newly discovered inodes"), /* 4 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
+{FMT1, _("setting up duplicate extent list"), /* 5 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
+{FMT1, _("initialize realtime bitmap"), /* 6 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_BLOCK]},
+{FMT1, _("reset realtime bitmaps"), /* 7 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
+{FMT1, _("check for inodes claiming duplicate blocks"), /* 8 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]},
+{FMT1, _("rebuild AG headers and trees"), /* 9 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
+{FMT1, _("traversing filesystem"), /* 10 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_AG]},
+{FMT2, _("traversing all unattached subtrees"), /* 11 */
+ &rpt_fmts[FMT2], &rpt_types[TYPE_DIR]},
+{FMT2, _("moving disconnected inodes to lost+found"), /* 12 */
+ &rpt_fmts[FMT2], &rpt_types[TYPE_INODE]},
+{FMT1, _("verify and correct link counts"), /* 13 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]},
+{FMT1, _("verify link counts"), /* 14 */
+ &rpt_fmts[FMT1], &rpt_types[TYPE_INODE]}
+};
+
+pthread_t report_thread;
+
+typedef struct msg_block_s {
+ pthread_mutex_t mutex;
+ progress_rpt_t *format;
+ __uint64_t *done;
+ __uint64_t *total;
+ int count;
+ int interval;
+} msg_block_t;
+static msg_block_t global_msgs;
+
+typedef struct phase_times_s {
+ time_t start;
+ time_t end;
+ time_t duration;
+ __uint64_t item_counts[4];
+} phase_times_t;
+static phase_times_t phase_times[8];
+
+static void *progress_rpt_thread(void *);
+static int current_phase;
+static int running;
+static __uint64_t prog_rpt_total;
+
+void
+init_progress_rpt (void)
+{
+
+ /*
+ * allocate the done vector
+ */
+
+ if ((prog_rpt_done = (__uint64_t *)
+ malloc(sizeof(__uint64_t)*glob_agcount)) == NULL ) {
+ do_error(_("cannot malloc pointer to done vector\n"));
+ }
+ bzero(prog_rpt_done, sizeof(__uint64_t)*glob_agcount);
+
+ /*
+ * Setup comm block, start the thread
+ */
+
+ pthread_mutex_init(&global_msgs.mutex, NULL);
+ global_msgs.count = glob_agcount;
+ global_msgs.interval = report_interval;
+ global_msgs.done = prog_rpt_done;
+ global_msgs.total = &prog_rpt_total;
+
+ if (pthread_create (&report_thread, NULL,
+ progress_rpt_thread, (void *)&global_msgs))
+ do_error(_("unable to create progress report thread\n"));
+
+ return;
+}
+
+void
+stop_progress_rpt(void)
+{
+
+ /*
+ * Tell msg thread to shutdown,
+ * wait for all threads to finished
+ */
+
+ running = 0;
+ pthread_kill (report_thread, SIGHUP);
+ pthread_join (report_thread, NULL);
+ free(prog_rpt_done);
+ return;
+}
+
+static void *
+progress_rpt_thread (void *p)
+{
+
+ int i;
+ int caught;
+ sigset_t sigs_to_catch;
+ struct tm *tmp;
+ time_t now, elapsed;
+ timer_t timerid;
+ struct itimerspec timespec;
+ char *msgbuf;
+ __uint64_t *donep;
+ __uint64_t sum;
+ msg_block_t *msgp = (msg_block_t *)p;
+ __uint64_t percent;
+
+ if ((msgbuf = (char *)malloc(DURATION_BUF_SIZE)) == NULL)
+ do_error (_("progress_rpt: cannot malloc progress msg buffer\n"));
+
+ running = 1;
+
+ /*
+ * Specify a repeating timer that fires each MSG_INTERVAL seconds.
+ */
+
+ timespec.it_value.tv_sec = msgp->interval;
+ timespec.it_value.tv_nsec = 0;
+ timespec.it_interval.tv_sec = msgp->interval;
+ timespec.it_interval.tv_nsec = 0;
+
+ if (timer_create (CLOCK_REALTIME, NULL, &timerid))
+ do_error(_("progress_rpt: cannot create timer\n"));
+
+ if (timer_settime (timerid, 0, ×pec, NULL))
+ do_error(_("progress_rpt: cannot set timer\n"));
+
+ /*
+ * Main loop - output messages based on periodic signal arrival
+ * set this thread's signal mask to block out all other signals
+ */
+
+ sigemptyset (&sigs_to_catch);
+ sigaddset (&sigs_to_catch, SIGALRM);
+ sigaddset (&sigs_to_catch, SIGHUP);
+ sigwait (&sigs_to_catch, &caught);
+
+ while (caught != SIGHUP) {
+ /*
+ * Allow the mainline to hold off messages by holding
+ * the lock. We don't want to just skip a period in case the
+ * reporting interval is very long... people get nervous. But,
+ * if the interval is very short, we can't let the timer go
+ * off again without sigwait'ing for it. So disarm the timer
+ * while we try to get the lock and giveup the cpu... the
+ * mainline shouldn't take that long.
+ */
+
+ if (pthread_mutex_lock(&msgp->mutex)) {
+ do_error(_("progress_rpt: cannot lock progress mutex\n"));
+ }
+
+ if (!running)
+ break;
+
+ now = time (NULL);
+ tmp = localtime ((const time_t *) &now);
+
+ /*
+ * Sum the work
+ */
+
+ sum = 0;
+ donep = msgp->done;
+ for (i = 0; i < msgp->count; i++) {
+ sum += *donep++;
+ }
+
+ percent = 0;
+ switch(msgp->format->format) {
+ case FMT1:
+ if (*msgp->total)
+ percent = (sum * 100) / ( *msgp->total );
+ sprintf (msgbuf, *msgp->format->fmt,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
+ msgp->format->msg, sum,
+ *msgp->total, *msgp->format->type);
+ break;
+ case FMT2:
+ sprintf (msgbuf, *msgp->format->fmt,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
+ msgp->format->msg, sum,
+ *msgp->format->type);
+ break;
+ }
+
+ do_log(_("%s"), msgbuf);
+ elapsed = now - phase_times[current_phase].start;
+ if ((msgp->format->format == FMT1) && sum && elapsed &&
+ ((current_phase == 3) ||
+ (current_phase == 4) ||
+ (current_phase == 7))) {
+ /* for inode phase report % complete */
+ do_log(
+ _("\t- %02d:%02d:%02d: Phase %d: elapsed time %s - processed %d %s per minute\n"),
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
+ current_phase, duration(elapsed, msgbuf),
+ (int) (60*sum/(elapsed)), *msgp->format->type);
+ do_log(
+ _("\t- %02d:%02d:%02d: Phase %d: %llu%% done - estimated remaining time %s\n"),
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
+ current_phase, percent,
+ duration((int) ((*msgp->total - sum) * (elapsed)/sum), msgbuf));
+ }
+
+ if (pthread_mutex_unlock(&msgp->mutex) != 0) {
+ do_error(
+ _("progress_rpt: error unlock msg mutex\n"));
+ }
+ sigwait (&sigs_to_catch, &caught);
+ }
+
+ if (timer_delete (timerid))
+ do_warn(_("cannot delete timer\n"));
+
+ free (msgbuf);
+ return (NULL);
+}
+
+int
+set_progress_msg (int report, __uint64_t total)
+{
+
+ if (!do_parallel)
+ return (0);
+
+ if (pthread_mutex_lock(&global_msgs.mutex))
+ do_error(_("set_progress_msg: cannot lock progress mutex\n"));
+
+ prog_rpt_total = total;
+ global_msgs.format = &progress_rpt_reports[report];
+
+ /* reset all the accumulative totals */
+ if (prog_rpt_done)
+ bzero(prog_rpt_done, sizeof(__uint64_t)*glob_agcount);
+
+ if (pthread_mutex_unlock(&global_msgs.mutex))
+ do_error(_("set_progress_msg: cannot unlock progress mutex\n"));
+
+ return (0);
+}
+
+__uint64_t
+print_final_rpt(void)
+{
+ int i;
+ struct tm *tmp;
+ time_t now;
+ __uint64_t *donep;
+ __uint64_t sum;
+ msg_block_t *msgp = &global_msgs;
+ char msgbuf[DURATION_BUF_SIZE];
+
+ if (!do_parallel)
+ return 0;
+
+ if (pthread_mutex_lock(&global_msgs.mutex))
+ do_error(_("print_final_rpt: cannot lock progress mutex\n"));
+
+ bzero(&msgbuf, sizeof(msgbuf));
+
+ now = time (NULL);
+ tmp = localtime ((const time_t *) &now);
+
+ /*
+ * Sum the work
+ */
+
+ sum = 0;
+ donep = msgp->done;
+ for (i = 0; i < msgp->count; i++) {
+ sum += *donep++;
+ }
+
+ if (report_interval) {
+ switch(msgp->format->format) {
+ case FMT1:
+ sprintf (msgbuf, *msgp->format->fmt,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
+ msgp->format->msg, sum,
+ *msgp->total, *msgp->format->type);
+ break;
+ case FMT2:
+ sprintf (msgbuf, *msgp->format->fmt,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
+ msgp->format->msg, sum,
+ *msgp->format->type);
+ break;
+ }
+ do_log(_("%s"), msgbuf);
+ }
+
+ if (pthread_mutex_unlock(&global_msgs.mutex))
+ do_error(_("print_final_rpt: cannot unlock progress mutex\n"));
+
+ return(sum);
+}
+
+void
+timediff(int phase)
+{
+ phase_times[phase].duration =
+ phase_times[phase].end - phase_times[phase].start;
+
+}
+
+/*
+** Get the time and save in the phase time
+** array.
+*/
+char *
+timestamp(int end, int phase, char *buf)
+{
+
+ time_t now;
+ struct tm *tmp;
+
+ now = time(NULL);
+
+ if (end) {
+ phase_times[phase].end = now;
+ timediff(phase);
+
+ /* total time in slot zero */
+ phase_times[0].end = now;
+ timediff(0);
+
+ if (phase < 7) {
+ phase_times[phase+1].start = now;
+ current_phase = phase + 1;
+ }
+ }
+ else {
+ phase_times[phase].start = now;
+ current_phase = phase;
+ }
+
+ if (buf) {
+ tmp = localtime((const time_t *)&now);
+ sprintf(buf, _("%02d:%02d:%02d"), tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ }
+ return(buf);
+}
+
+char *
+duration(int length, char *buf)
+{
+ int sum;
+ int weeks;
+ int days;
+ int hours;
+ int minutes;
+ int seconds;
+ char temp[128];
+
+ *buf = '\0';
+ weeks = days = hours = minutes = seconds = sum = 0;
+ if (length >= ONEWEEK) {
+ weeks = length / ONEWEEK;
+ sum = (weeks * ONEWEEK);
+ if (weeks) {
+ sprintf(buf, _("%d week"), weeks);
+ if (weeks > 1) strcat(buf, _("s"));
+ if ((length-sum) == 0)
+ return(buf);
+ }
+ }
+ if (length >= ONEDAY) {
+ days = (length - sum) / ONEDAY;
+ sum += (days * ONEDAY);
+ if (days) {
+ sprintf(temp, _("%d day"), days);
+ if (days > 1) strcat(temp, _("s"));
+ if (((length-sum) == 0) && (!weeks)) {
+ strcpy(buf, temp);
+ return(buf);
+ }
+ else if (weeks) {
+ strcat(buf, _(", "));
+ }
+ strcat(buf, temp);
+ }
+ }
+ if (length >= ONEHOUR) {
+ hours = (length - sum) / ONEHOUR;
+ sum += (hours * ONEHOUR);
+ if (hours) {
+ sprintf(temp, _("%d hour"), hours);
+ if (hours > 1) strcat(temp, _("s"));
+ if (((length-sum) == 0) &&
+ (!weeks) && (!days)) {
+ strcpy(buf, temp);
+ return(buf);
+ }
+ else if ((weeks) || (days)) {
+ strcat(buf, _(", "));
+ }
+ strcat(buf, temp);
+ }
+
+ }
+ if (length >= ONEMINUTE) {
+ minutes = (length - sum) / ONEMINUTE;
+ sum += (minutes * ONEMINUTE);
+ if (minutes) {
+ sprintf(temp, _("%d minute"), minutes);
+ if (minutes > 1) strcat(temp, _("s"));
+ if (((length-sum) == 0) &&
+ (!weeks) && (!days) && (!hours)) {
+ strcpy(buf, temp);
+ return(buf);
+ }
+ else if ((weeks)||(days)||(hours)) {
+ strcat(buf, _(", "));
+ }
+ strcat(buf, temp);
+ }
+ }
+ seconds = length - sum;
+ if (seconds) {
+ sprintf(temp, _("%d second"), seconds);
+ if (seconds > 1) strcat(temp, _("s"));
+ if ((weeks)||(days)||(hours)||(minutes))
+ strcat(buf, _(", "));
+ strcat(buf, temp);
+ }
+
+ return(buf);
+}
+
+void
+summary_report(void)
+{
+ int i;
+ time_t now;
+ struct tm end;
+ struct tm start;
+ char msgbuf[DURATION_BUF_SIZE];
+
+ now = time(NULL);
+
+ do_log(_("\n XFS_REPAIR Summary %s\n"),
+ ctime((const time_t *)&now));
+ do_log(_("Phase\t\tStart\t\tEnd\t\tDuration\n"));
+ for (i = 1; i < 8; i++) {
+ localtime_r((const time_t *)&phase_times[i].start, &start);
+ localtime_r((const time_t *)&phase_times[i].end, &end);
+ if ((no_modify) && (i == 5)) {
+ do_log(_("Phase %d:\tSkipped\n"), i);
+ }
+ else if ((bad_ino_btree) && ((i == 6) || (i == 7))) {
+ do_log(_("Phase %d:\tSkipped\n"), i);
+ }
+ else {
+ do_log(
+ _("Phase %d:\t%02d/%02d %02d:%02d:%02d\t%02d/%02d %02d:%02d:%02d\t%s\n"), i,
+ start.tm_mon+1, start.tm_mday, start.tm_hour, start.tm_min, start.tm_sec,
+ end.tm_mon+1, end.tm_mday, end.tm_hour, end.tm_min, end.tm_sec,
+ duration(phase_times[i].duration, msgbuf));
+ }
+ }
+ do_log(_("\nTotal run time: %s\n"), duration(phase_times[0].duration, msgbuf));
+}
#include "err_protos.h"
#include "prefetch.h"
#include "threads.h"
+#include "progress.h"
#define rounddown(x, y) (((x)/(y))*(y))
NULL
};
-static void print_runtime(unsigned);
-
static void
usage(void)
{
fs_has_extflgbit_allowed = 1;
pre_65_beta = 0;
fs_shared_allowed = 1;
+ report_interval = PROG_RPT_DEFAULT;
/*
* XXX have to add suboption processing here
* attributes, quotas, nlinks, aligned_inos, sb_fbits
*/
- while ((c = getopt(argc, argv, "o:fl:r:LnDvVdPM")) != EOF) {
+ while ((c = getopt(argc, argv, "o:fl:r:LnDvVdPMt:")) != EOF) {
switch (c) {
case 'D':
dumpcore = 1;
case 'M':
do_parallel ^= 1;
break;
+ case 't':
+ report_interval = (int) strtol(optarg, 0, 0);
+ break;
+
case '?':
usage();
}
xfs_sb_t *sb;
xfs_buf_t *sbp;
xfs_mount_t xfs_m;
- time_t t, start;
+ char *msgbuf;
- start = time(NULL);
progname = basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
process_args(argc, argv);
xfs_init(&x);
+ timestamp(PHASE_START, 0, NULL);
+ timestamp(PHASE_END, 0, NULL);
+
/* do phase1 to make sure we have a superblock */
phase1(temp_mp);
- if (verbose) {
- t = time(NULL);
- fprintf(stderr, asctime(localtime(&t)));
- }
+ timestamp(PHASE_END, 1, NULL);
if (no_modify && primary_sb_modified) {
do_warn(_("Primary superblock would have been modified.\n"
max_symlink_blocks = howmany(MAXPATHLEN - 1, mp->m_sb.sb_blocksize);
inodes_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog;
+ if (do_parallel && report_interval) {
+ init_progress_rpt();
+ msgbuf = malloc(DURATION_BUF_SIZE);
+ if (msgbuf) {
+ do_log(_(" - reporting progress in intervals of %s\n"),
+ duration(report_interval, msgbuf));
+ free(msgbuf);
+ }
+ }
+
/*
* calculate what mkfs would do to this filesystem
*/
/* make sure the per-ag freespace maps are ok so we can mount the fs */
phase2(mp);
- if (verbose) {
- t = time(NULL);
- fprintf(stderr, asctime(localtime(&t)));
- }
+ timestamp(PHASE_END, 2, NULL);
phase3(mp);
- if (verbose) {
- t = time(NULL);
- fprintf(stderr, asctime(localtime(&t)));
- }
+ timestamp(PHASE_END, 3, NULL);
phase4(mp);
- if (verbose) {
- t = time(NULL);
- fprintf(stderr, asctime(localtime(&t)));
- }
+ timestamp(PHASE_END, 4, NULL);
/* XXX: nathans - something in phase4 ain't playing by */
/* the buffer cache rules.. why doesn't IRIX hit this? */
printf(_("No modify flag set, skipping phase 5\n"));
else {
phase5(mp);
- if (verbose) {
- t = time(NULL);
- fprintf(stderr, asctime(localtime(&t)));
- }
}
+ timestamp(PHASE_END, 5, NULL);
if (!bad_ino_btree) {
phase6(mp);
- if (verbose) {
- t = time(NULL);
- fprintf(stderr, asctime(localtime(&t)));
- }
+ timestamp(PHASE_END, 6, NULL);
phase7(mp);
- if (verbose) {
- t = time(NULL);
- fprintf(stderr, asctime(localtime(&t)));
- }
+ timestamp(PHASE_END, 7, NULL);
} else {
do_warn(
_("Inode allocation btrees are too corrupted, skipping phases 6 and 7\n"));
}
}
- if (verbose > 1)
- libxfs_report(stderr);
+ if (do_parallel && report_interval)
+ stop_progress_rpt();
if (no_modify) {
do_log(
_("No modify flag set, skipping filesystem flush and exiting.\n"));
if (verbose)
- print_runtime(t - start);
+ summary_report();
if (fs_is_dirty)
return(1);
libxfs_device_close(x.logdev);
libxfs_device_close(x.ddev);
+ if (verbose)
+ summary_report();
do_log(_("done\n"));
- if (verbose) {
- print_runtime(t - start);
- }
return (0);
}
-
-static void
-print_runtime(unsigned s)
-{
- unsigned h, m;
-
- h = s / 3600;
- s %= 3600;
- m = s / 60;
- s %= 60;
- if (h) {
- fprintf(stderr, "Run time %d hour%s %d minute%s %d second%s\n",
- h, h > 1 ? "s" : "",
- m, m != 1 ? "s" : "",
- s, s != 1 ? "s" : "");
- } else if (m) {
- fprintf(stderr, "Run time %d minute%s %d second%s\n",
- m, m > 1 ? "s" : "",
- s, s != 1 ? "s" : "");
- }
- else {
- fprintf(stderr, "Run time %d second%s\n",
- s, s != 1 ? "s" : "");
- }
-}