1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
9 #include <sys/statvfs.h>
11 #include "libfrog/paths.h"
13 #include "read_verify.h"
14 #include "xfs_scrub.h"
22 * For scrub phases that expect to take a long time, this facility uses
23 * the threaded counter and some phase/state information to report the
24 * progress of a particular phase to stdout. Each phase that wants
25 * progress information needs to set up the tracker with an estimate of
26 * the work to be done and periodic updates when work items finish. In
27 * return, the progress tracker will print a pretty progress bar and
28 * twiddle to a tty, or a raw numeric output compatible with fsck -C.
30 struct progress_tracker
{
33 struct ptcounter
*ptc
;
44 pthread_cond_t wakeup
;
47 static struct progress_tracker pt
= {
48 .lock
= PTHREAD_MUTEX_INITIALIZER
,
49 .wakeup
= PTHREAD_COND_INITIALIZER
,
52 /* Add some progress. */
58 ptcounter_add(pt
.ptc
, x
);
61 static const char twiddles
[] = "|/-\\";
79 /* Emulate fsck machine-readable output (phase, cur, max, label) */
81 snprintf(buf
, sizeof(buf
), _("%u %"PRIu64
" %"PRIu64
" %s"),
82 pt
.phase
, sum
, pt
.max
, pt
.tag
);
83 fprintf(pt
.fp
, "%s\n", buf
);
88 /* Interactive twiddle progress bar. */
90 num_len
= snprintf(buf
, sizeof(buf
),
91 "%c %"PRIu64
"/%"PRIu64
" (%.1f%%)",
95 100.0 * sum
/ pt
.max
);
97 num_len
= snprintf(buf
, sizeof(buf
),
100 100.0 * sum
/ pt
.max
);
102 memmove(buf
+ sizeof(buf
) - (num_len
+ 1), buf
, num_len
+ 1);
103 tag_len
= snprintf(buf
, sizeof(buf
), _("Phase %u: |"), pt
.phase
);
104 pbar_len
= sizeof(buf
) - (num_len
+ 1 + tag_len
);
105 plen
= (int)((double)pbar_len
* sum
/ pt
.max
);
106 memset(buf
+ tag_len
, '=', plen
);
107 memset(buf
+ tag_len
+ plen
, ' ', pbar_len
- plen
);
108 pt
.twiddle
= (pt
.twiddle
+ 1) % 4;
109 fprintf(pt
.fp
, "%c%s\r%c", START_IGNORE
, buf
, END_IGNORE
);
114 progress_report_thread(void *arg
)
116 struct timespec abstime
;
119 rcu_register_thread();
120 pthread_mutex_lock(&pt
.lock
);
122 uint64_t progress_val
;
124 /* Every half second. */
125 ret
= clock_gettime(CLOCK_REALTIME
, &abstime
);
128 abstime
.tv_nsec
+= NSEC_PER_SEC
/ 2;
129 if (abstime
.tv_nsec
> NSEC_PER_SEC
) {
131 abstime
.tv_nsec
-= NSEC_PER_SEC
;
133 ret
= pthread_cond_timedwait(&pt
.wakeup
, &pt
.lock
, &abstime
);
134 if (ret
&& ret
!= ETIMEDOUT
)
138 ret
= ptcounter_value(pt
.ptc
, &progress_val
);
140 progress_report(progress_val
);
142 pthread_mutex_unlock(&pt
.lock
);
143 rcu_unregister_thread();
147 /* End a phase of progress reporting. */
149 progress_end_phase(void)
154 pthread_mutex_lock(&pt
.lock
);
156 pthread_mutex_unlock(&pt
.lock
);
157 pthread_cond_broadcast(&pt
.wakeup
);
158 pthread_join(pt
.thread
, NULL
);
160 progress_report(pt
.max
);
161 ptcounter_free(pt
.ptc
);
165 fprintf(pt
.fp
, CLEAR_EOL
);
172 * Set ourselves up to report progress. If errors are encountered, this
173 * function will log them and return nonzero.
177 struct scrub_ctx
*ctx
,
182 unsigned int nr_threads
)
186 assert(pt
.fp
== NULL
);
187 if (fp
== NULL
|| max
== 0) {
192 pt
.isatty
= isatty(fileno(fp
));
193 pt
.tag
= ctx
->mntpoint
;
198 pt
.terminate
= false;
200 ret
= ptcounter_alloc(nr_threads
, &pt
.ptc
);
202 str_liberror(ctx
, ret
, _("allocating progress counter"));
206 ret
= pthread_create(&pt
.thread
, NULL
, progress_report_thread
, NULL
);
208 str_liberror(ctx
, ret
, _("creating progress reporting thread"));
215 ptcounter_free(pt
.ptc
);