From: Madan Valluri Date: Fri, 29 Sep 2006 17:16:48 +0000 (+0000) Subject: Progress reporting for lengthy runs. X-Git-Tag: v2.9.0~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=06fbdda991bb75dc33bd029474dae60b8fd410ff;p=thirdparty%2Fxfsprogs-dev.git Progress reporting for lengthy runs. Added progress.c and progress.h --- diff --git a/repair/Makefile b/repair/Makefile index 835c0d3fb..cfa172cd2 100644 --- a/repair/Makefile +++ b/repair/Makefile @@ -9,13 +9,13 @@ LTCOMMAND = xfs_repair HFILES = agheader.h attr_repair.h avl.h avl64.h bmap.h dinode.h dir.h \ dir2.h dir_stack.h err_protos.h globals.h incore.h protos.h rt.h \ - scan.h versions.h prefetch.h threads.h + progress.h scan.h versions.h prefetch.h threads.h CFILES = agheader.c attr_repair.c avl.c avl64.c bmap.c dino_chunks.c \ dinode.c dir.c dir2.c dir_stack.c globals.c incore.c \ incore_bmc.c init.c incore_ext.c incore_ino.c phase1.c \ phase2.c phase3.c phase4.c phase5.c phase6.c phase7.c rt.c sb.c \ - prefetch.c scan.c versions.c xfs_repair.c threads.c + progress.c prefetch.c scan.c versions.c xfs_repair.c threads.c LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBUUID) $(LIBPTHREAD) $(LIBRT) LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) diff --git a/repair/dino_chunks.c b/repair/dino_chunks.c index 0b16db669..e80e1b8ac 100644 --- a/repair/dino_chunks.c +++ b/repair/dino_chunks.c @@ -28,6 +28,7 @@ #include "prefetch.h" #include "threads.h" #include "versions.h" +#include "progress.h" /* * validates inode block or chunk, returns # of good inodes @@ -998,6 +999,7 @@ process_aginodes(xfs_mount_t *mp, xfs_agnumber_t agno, first_ino_rec = ino_rec; } + PROG_RPT_INC(prog_rpt_done[agno], num_inos); } } diff --git a/repair/globals.h b/repair/globals.h index f581bcf16..a4727a0b1 100644 --- a/repair/globals.h +++ b/repair/globals.h @@ -197,4 +197,7 @@ extern size_t ts_attr_freemap_size; EXTERN pthread_rwlock_t *per_ag_lock; +EXTERN int report_interval; +EXTERN __uint64_t *prog_rpt_done; + #endif /* _XFS_REPAIR_GLOBAL_H */ diff --git a/repair/phase2.c b/repair/phase2.c index 0d689bc5d..e9adef25d 100644 --- a/repair/phase2.c +++ b/repair/phase2.c @@ -23,6 +23,7 @@ #include "protos.h" #include "err_protos.h" #include "incore.h" +#include "progress.h" void set_mp(xfs_mount_t *mpp); void scan_ag(xfs_agnumber_t agno); @@ -131,6 +132,8 @@ phase2(xfs_mount_t *mp) bad_ino_btree = 0; + set_progress_msg(PROG_FMT_SCAN_AG, (__uint64_t) glob_agcount); + for (i = 0; i < mp->m_sb.sb_agcount; i++) { scan_ag(i); #ifdef XR_INODE_TRACE @@ -138,6 +141,8 @@ phase2(xfs_mount_t *mp) #endif } + print_final_rpt(); + /* * make sure we know about the root inode chunk */ diff --git a/repair/phase3.c b/repair/phase3.c index 8a50bdfe8..01ff95de6 100644 --- a/repair/phase3.c +++ b/repair/phase3.c @@ -25,6 +25,7 @@ #include "err_protos.h" #include "dinode.h" #include "threads.h" +#include "progress.h" /* * walks an unlinked list, returns 1 on an error (bogus pointer) or @@ -170,6 +171,8 @@ phase3(xfs_mount_t *mp) else do_log(_(" - scan (but don't clear) agi unlinked lists...\n")); + set_progress_msg(PROG_FMT_AGI_UNLINKED, (__uint64_t) glob_agcount); + /* * first, let's look at the possibly bogus inodes */ @@ -179,22 +182,27 @@ phase3(xfs_mount_t *mp) */ process_agi_unlinked(mp, i); check_uncertain_aginodes(mp, i); + PROG_RPT_INC(prog_rpt_done[i], 1); } + print_final_rpt(); /* ok, now that the tree's ok, let's take a good look */ do_log(_( " - process known inodes and perform inode discovery...\n")); + set_progress_msg(PROG_FMT_PROCESS_INO, (__uint64_t) mp->m_sb.sb_icount); for (i = 0; i < mp->m_sb.sb_agcount; i++) { queue_work(parallel_p3_process_aginodes, mp, i); } wait_for_workers(); + print_final_rpt(); /* * process newly discovered inode chunks */ do_log(_(" - process newly discovered inodes...\n")); + set_progress_msg(PROG_FMT_NEW_INODES, (__uint64_t) glob_agcount); do { /* * have to loop until no ag has any uncertain @@ -207,6 +215,8 @@ phase3(xfs_mount_t *mp) fprintf(stderr, "\t\t phase 3 - process_uncertain_inodes returns %d\n", j); #endif + PROG_RPT_INC(prog_rpt_done[i], 1); } } while (j != 0); + print_final_rpt(); } diff --git a/repair/phase4.c b/repair/phase4.c index f65b5db77..52e447e6e 100644 --- a/repair/phase4.c +++ b/repair/phase4.c @@ -29,6 +29,7 @@ #include "versions.h" #include "dir2.h" #include "threads.h" +#include "progress.h" /* ARGSUSED */ @@ -1154,6 +1155,8 @@ phase4(xfs_mount_t *mp) do_log(_("Phase 4 - check for duplicate blocks...\n")); do_log(_(" - setting up duplicate extent list...\n")); + set_progress_msg(PROG_FMT_DUP_EXTENT, (__uint64_t) glob_agcount); + irec = find_inode_rec(XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino), XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino)); @@ -1251,7 +1254,9 @@ phase4(xfs_mount_t *mp) */ if (extent_start != 0) add_dup_extent(i, extent_start, extent_len); + PROG_RPT_INC(prog_rpt_done[i], 1); } + print_final_rpt(); /* * initialize realtime bitmap @@ -1328,6 +1333,7 @@ phase4(xfs_mount_t *mp) set_bmap_fs(mp); do_log(_(" - check for inodes claiming duplicate blocks...\n")); + set_progress_msg(PROG_FMT_DUP_BLOCKS, (__uint64_t) mp->m_sb.sb_icount); for (i = 0; i < mp->m_sb.sb_agcount; i++) { /* * ok, now process the inodes -- signal 2-pass check per inode. @@ -1341,6 +1347,7 @@ phase4(xfs_mount_t *mp) queue_work(parallel_p4_process_aginodes, mp, i); } wait_for_workers(); + print_final_rpt(); /* * free up memory used to track trealtime duplicate extents diff --git a/repair/phase5.c b/repair/phase5.c index 471f2f16f..aa83d5910 100644 --- a/repair/phase5.c +++ b/repair/phase5.c @@ -27,6 +27,7 @@ #include "rt.h" #include "versions.h" #include "threads.h" +#include "progress.h" /* * we maintain the current slice (path from root to leaf) @@ -1591,6 +1592,7 @@ phase5_function(xfs_mount_t *mp, xfs_agnumber_t agno) release_agbno_extent_tree(agno); release_agbcnt_extent_tree(agno); } + PROG_RPT_INC(prog_rpt_done[agno], 1); } void @@ -1599,6 +1601,7 @@ phase5(xfs_mount_t *mp) xfs_agnumber_t agno; do_log(_("Phase 5 - rebuild AG headers and trees...\n")); + set_progress_msg(PROG_FMT_REBUILD_AG, (__uint64_t )glob_agcount); #ifdef XR_BLD_FREE_TRACE fprintf(stderr, "inobt level 1, maxrec = %d, minrec = %d\n", @@ -1642,6 +1645,7 @@ phase5(xfs_mount_t *mp) queue_work(phase5_function, mp, agno); } wait_for_workers(); + print_final_rpt(); /* aggregate per ag counters */ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) { diff --git a/repair/phase6.c b/repair/phase6.c index 1dbdc7630..c0098b54c 100644 --- a/repair/phase6.c +++ b/repair/phase6.c @@ -28,6 +28,7 @@ #include "err_protos.h" #include "dinode.h" #include "prefetch.h" +#include "progress.h" #include "versions.h" static struct cred zerocr; @@ -3861,9 +3862,12 @@ traverse_alt(xfs_mount_t *mp) { int i; + set_progress_msg(PROG_FMT_TRAVERSAL, (__uint64_t) glob_agcount); for (i = 0; i < mp->m_sb.sb_agcount; i++) { traverse_function(mp, i); + PROG_RPT_INC(prog_rpt_done[i], 1); } + print_final_rpt(); } void diff --git a/repair/phase7.c b/repair/phase7.c index e4b958638..1ab8c9aae 100644 --- a/repair/phase7.c +++ b/repair/phase7.c @@ -26,6 +26,7 @@ #include "dinode.h" #include "versions.h" #include "prefetch.h" +#include "progress.h" #include "threads.h" /* dinoc is a pointer to the IN-CORE dinode core */ @@ -170,6 +171,7 @@ phase7_alt_function(xfs_mount_t *mp, xfs_agnumber_t agno) libxfs_putbuf(bp); irec = next_ino_rec(irec); + PROG_RPT_INC(prog_rpt_done[agno], XFS_INODES_PER_CHUNK); } } @@ -178,12 +180,15 @@ phase7_alt(xfs_mount_t *mp) { int i; + set_progress_msg(no_modify ? PROGRESS_FMT_VRFY_LINK : PROGRESS_FMT_CORR_LINK, + (__uint64_t) mp->m_sb.sb_icount); libxfs_bcache_purge(); for (i = 0; i < glob_agcount; i++) { queue_work(phase7_alt_function, mp, i); } wait_for_workers(); + print_final_rpt(); } void diff --git a/repair/progress.c b/repair/progress.c new file mode 100644 index 000000000..2abaed57a --- /dev/null +++ b/repair/progress.c @@ -0,0 +1,527 @@ + +#include +#include "progress.h" +#include "globals.h" +#include "err_protos.h" +#include + +#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)); +} diff --git a/repair/progress.h b/repair/progress.h new file mode 100644 index 000000000..2f7ca2eef --- /dev/null +++ b/repair/progress.h @@ -0,0 +1,43 @@ +#ifndef _XFS_REPAIR_PROGRESS_RPT_H_ +#define _XFS_REPAIR_PROGRESS_RPT_H_ + +#define PROG_RPT_DEFAULT (15*60) /* default 15 minute report interval */ +#define PHASE_START 0 +#define PHASE_END 1 + + +#define PROG_FMT_SCAN_AG 0 /* Phase 2 */ + +#define PROG_FMT_AGI_UNLINKED 1 /* Phase 3 */ +#define PROG_FMT_UNCERTAIN 2 +#define PROG_FMT_PROCESS_INO 3 +#define PROG_FMT_NEW_INODES 4 + +#define PROG_FMT_DUP_EXTENT 5 /* Phase 4 */ +#define PROG_FMT_INIT_RTEXT 6 +#define PROG_FMT_RESET_RTBM 7 +#define PROG_FMT_DUP_BLOCKS 8 + +#define PROG_FMT_REBUILD_AG 9 /* Phase 5 */ + +#define PROG_FMT_TRAVERSAL 10 /* Phase 6 */ +#define PROG_FMT_TRAVERSSUB 11 +#define PROG_FMT_DISCONINODE 12 + +#define PROGRESS_FMT_CORR_LINK 13 /* Phase 7 */ +#define PROGRESS_FMT_VRFY_LINK 14 + +#define DURATION_BUF_SIZE 512 + +extern void init_progress_rpt(void); +extern void stop_progress_rpt(void); +extern void summary_report(void); +extern int set_progress_msg(int report, __uint64_t total); +extern __uint64_t print_final_rpt(void); +extern char *timestamp(int end, int phase, char *buf); +extern char *duration(int val, char *buf); +extern int do_parallel; + +#define PROG_RPT_INC(a,b) if (do_parallel && prog_rpt_done) (a) += (b) + +#endif /* _XFS_REPAIR_PROGRESS_RPT_H_ */ diff --git a/repair/scan.c b/repair/scan.c index a85e5180d..0212e40b4 100644 --- a/repair/scan.c +++ b/repair/scan.c @@ -28,6 +28,7 @@ #include "versions.h" #include "bmap.h" #include "prefetch.h" +#include "progress.h" extern int verify_set_agheader(xfs_mount_t *mp, xfs_buf_t *sbuf, xfs_sb_t *sb, xfs_agf_t *agf, xfs_agi_t *agi, xfs_agnumber_t i); @@ -1299,4 +1300,5 @@ scan_ag( } else libxfs_putbuf(sbbuf); free(sb); + PROG_RPT_INC(prog_rpt_done[agno], 1); } diff --git a/repair/xfs_repair.c b/repair/xfs_repair.c index 398cedc3f..c5d82ad84 100644 --- a/repair/xfs_repair.c +++ b/repair/xfs_repair.c @@ -27,6 +27,7 @@ #include "err_protos.h" #include "prefetch.h" #include "threads.h" +#include "progress.h" #define rounddown(x, y) (((x)/(y))*(y)) @@ -69,8 +70,6 @@ char *o_opts[] = { NULL }; -static void print_runtime(unsigned); - static void usage(void) { @@ -187,12 +186,13 @@ process_args(int argc, char **argv) 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; @@ -274,6 +274,10 @@ process_args(int argc, char **argv) case 'M': do_parallel ^= 1; break; + case 't': + report_interval = (int) strtol(optarg, 0, 0); + break; + case '?': usage(); } @@ -469,9 +473,8 @@ main(int argc, char **argv) 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); @@ -483,12 +486,12 @@ main(int argc, char **argv) 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" @@ -522,6 +525,16 @@ main(int argc, char **argv) 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 */ @@ -540,22 +553,13 @@ main(int argc, char **argv) /* 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? */ @@ -565,24 +569,15 @@ main(int argc, char **argv) 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")); @@ -642,14 +637,14 @@ _("Warning: project quota information would be cleared.\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); @@ -695,34 +690,8 @@ _("Note - stripe unit (%d) and width (%d) fields have been reset.\n" 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" : ""); - } -}