]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
Progress reporting for lengthy runs.
authorMadan Valluri <mvalluri@sgi.com>
Fri, 29 Sep 2006 17:16:48 +0000 (17:16 +0000)
committerMadan Valluri <mvalluri@sgi.com>
Fri, 29 Sep 2006 17:16:48 +0000 (17:16 +0000)
Added progress.c and progress.h

13 files changed:
repair/Makefile
repair/dino_chunks.c
repair/globals.h
repair/phase2.c
repair/phase3.c
repair/phase4.c
repair/phase5.c
repair/phase6.c
repair/phase7.c
repair/progress.c [new file with mode: 0644]
repair/progress.h [new file with mode: 0644]
repair/scan.c
repair/xfs_repair.c

index 835c0d3fb344d92c2bcd82dc115827ccc05bb4be..cfa172cd23fff1eb525871fff12dbe2e087cc45b 100644 (file)
@@ -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)
index 0b16db66952d14452d238d92cf2cfe4049eef416..e80e1b8ac6a4d23f29d21abb3e09d943c32b0fe9 100644 (file)
@@ -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);
        }
 }
 
index f581bcf166093327a63be97b16a67ab7a5674c6b..a4727a0b1b365d1403d64693381f4c5517e9946b 100644 (file)
@@ -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 */
index 0d689bc5d8eff24a6bcaef0a6e60e2a33ef8f16b..e9adef25dc2eee74019257e82b18f2e7cc6712b0 100644 (file)
@@ -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
         */
index 8a50bdfe8d6b5dd5d00bfc52f0af356d8e4e29c8..01ff95de67c10390f9b86dbbb7fdba465173470c 100644 (file)
@@ -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();
 }
index f65b5db7765a91b0a858d81addd48086af7eea02..52e447e6e98239b77602228aac121d8de7c9e99c 100644 (file)
@@ -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
index 471f2f16fa227458fe3827d43c79d768b472db65..aa83d5910913241c620c2ba6b6a4a6134985e1d0 100644 (file)
@@ -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++)  {
index 1dbdc7630c6a2e6a55047adf79329e31c6f8a254..c0098b54c814cba45332491282c305905b6e9d5a 100644 (file)
@@ -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
index e4b958638e567dff309d26dfef73f6025579f0f8..1ab8c9aae02799186c005b315d6532d51129e445 100644 (file)
@@ -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 (file)
index 0000000..2abaed5
--- /dev/null
@@ -0,0 +1,527 @@
+
+#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, &timespec, 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 (file)
index 0000000..2f7ca2e
--- /dev/null
@@ -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_ */
index a85e5180da4c3bc9070ce78ef27fbb4cf933468b..0212e40b4c078fe348b803408239fd4e6cd11375 100644 (file)
@@ -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);
 }
index 398cedc3f325d18e5f6e3d987ca7f96a0fd8496d..c5d82ad84f95bf174c7120b9cbb800b86eca5553 100644 (file)
@@ -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" : "");
-       }
-}