]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/progress.c
xfs_scrub_fail: advise recipients not to reply
[thirdparty/xfsprogs-dev.git] / scrub / progress.c
CommitLineData
8d318d62 1// SPDX-License-Identifier: GPL-2.0-or-later
ed60d210 2/*
52520522 3 * Copyright (C) 2018-2024 Oracle. All Rights Reserved.
8d318d62 4 * Author: Darrick J. Wong <djwong@kernel.org>
ed60d210 5 */
a440f877 6#include "xfs.h"
ed60d210
DW
7#include <dirent.h>
8#include <pthread.h>
9#include <sys/statvfs.h>
10#include <time.h>
42b4c8e8 11#include "libfrog/paths.h"
ed60d210
DW
12#include "disk.h"
13#include "read_verify.h"
14#include "xfs_scrub.h"
15#include "common.h"
16#include "counter.h"
17#include "progress.h"
18
19/*
20 * Progress Tracking
21 *
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.
29 */
30struct progress_tracker {
31 FILE *fp;
32 const char *tag;
33 struct ptcounter *ptc;
34 uint64_t max;
35 unsigned int phase;
36 int rshift;
37 int twiddle;
38 bool isatty;
39 bool terminate;
40 pthread_t thread;
41
42 /* static state */
43 pthread_mutex_t lock;
44 pthread_cond_t wakeup;
45};
46
47static struct progress_tracker pt = {
48 .lock = PTHREAD_MUTEX_INITIALIZER,
49 .wakeup = PTHREAD_COND_INITIALIZER,
50};
51
52/* Add some progress. */
53void
54progress_add(
55 uint64_t x)
56{
57 if (pt.fp)
58 ptcounter_add(pt.ptc, x);
59}
60
61static const char twiddles[] = "|/-\\";
62
63static void
64progress_report(
65 uint64_t sum)
66{
67 char buf[81];
68 int tag_len;
69 int num_len;
70 int pbar_len;
71 int plen;
72
73 if (!pt.fp)
74 return;
75
76 if (sum > pt.max)
77 sum = pt.max;
78
79 /* Emulate fsck machine-readable output (phase, cur, max, label) */
80 if (!pt.isatty) {
81 snprintf(buf, sizeof(buf), _("%u %"PRIu64" %"PRIu64" %s"),
82 pt.phase, sum, pt.max, pt.tag);
83 fprintf(pt.fp, "%s\n", buf);
84 fflush(pt.fp);
85 return;
86 }
87
88 /* Interactive twiddle progress bar. */
89 if (debug) {
90 num_len = snprintf(buf, sizeof(buf),
91 "%c %"PRIu64"/%"PRIu64" (%.1f%%)",
92 twiddles[pt.twiddle],
93 sum >> pt.rshift,
94 pt.max >> pt.rshift,
95 100.0 * sum / pt.max);
96 } else {
97 num_len = snprintf(buf, sizeof(buf),
98 "%c (%.1f%%)",
99 twiddles[pt.twiddle],
100 100.0 * sum / pt.max);
101 }
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);
110 fflush(pt.fp);
111}
112
ed60d210
DW
113static void *
114progress_report_thread(void *arg)
115{
116 struct timespec abstime;
117 int ret;
118
e4da1b16 119 rcu_register_thread();
ed60d210
DW
120 pthread_mutex_lock(&pt.lock);
121 while (1) {
da3dd6c0
DW
122 uint64_t progress_val;
123
ed60d210
DW
124 /* Every half second. */
125 ret = clock_gettime(CLOCK_REALTIME, &abstime);
126 if (ret)
127 break;
128 abstime.tv_nsec += NSEC_PER_SEC / 2;
129 if (abstime.tv_nsec > NSEC_PER_SEC) {
130 abstime.tv_sec++;
131 abstime.tv_nsec -= NSEC_PER_SEC;
132 }
8808a003
DW
133 ret = pthread_cond_timedwait(&pt.wakeup, &pt.lock, &abstime);
134 if (ret && ret != ETIMEDOUT)
135 break;
ed60d210
DW
136 if (pt.terminate)
137 break;
da3dd6c0
DW
138 ret = ptcounter_value(pt.ptc, &progress_val);
139 if (!ret)
140 progress_report(progress_val);
ed60d210
DW
141 }
142 pthread_mutex_unlock(&pt.lock);
e4da1b16 143 rcu_unregister_thread();
ed60d210
DW
144 return NULL;
145}
146
147/* End a phase of progress reporting. */
148void
149progress_end_phase(void)
150{
151 if (!pt.fp)
152 return;
153
154 pthread_mutex_lock(&pt.lock);
155 pt.terminate = true;
156 pthread_mutex_unlock(&pt.lock);
157 pthread_cond_broadcast(&pt.wakeup);
158 pthread_join(pt.thread, NULL);
159
160 progress_report(pt.max);
161 ptcounter_free(pt.ptc);
162 pt.max = 0;
163 pt.ptc = NULL;
164 if (pt.fp) {
165 fprintf(pt.fp, CLEAR_EOL);
166 fflush(pt.fp);
167 }
168 pt.fp = NULL;
169}
170
d86e83b8
DW
171/*
172 * Set ourselves up to report progress. If errors are encountered, this
173 * function will log them and return nonzero.
174 */
175int
ed60d210
DW
176progress_init_phase(
177 struct scrub_ctx *ctx,
178 FILE *fp,
179 unsigned int phase,
180 uint64_t max,
181 int rshift,
182 unsigned int nr_threads)
183{
184 int ret;
185
186 assert(pt.fp == NULL);
187 if (fp == NULL || max == 0) {
188 pt.fp = NULL;
d86e83b8 189 return 0;
ed60d210
DW
190 }
191 pt.fp = fp;
192 pt.isatty = isatty(fileno(fp));
193 pt.tag = ctx->mntpoint;
194 pt.max = max;
195 pt.phase = phase;
196 pt.rshift = rshift;
197 pt.twiddle = 0;
198 pt.terminate = false;
199
da3dd6c0
DW
200 ret = ptcounter_alloc(nr_threads, &pt.ptc);
201 if (ret) {
202 str_liberror(ctx, ret, _("allocating progress counter"));
ed60d210 203 goto out_max;
da3dd6c0 204 }
ed60d210
DW
205
206 ret = pthread_create(&pt.thread, NULL, progress_report_thread, NULL);
0a9ac205
DW
207 if (ret) {
208 str_liberror(ctx, ret, _("creating progress reporting thread"));
ed60d210 209 goto out_ptcounter;
0a9ac205 210 }
ed60d210 211
d86e83b8 212 return 0;
ed60d210
DW
213
214out_ptcounter:
215 ptcounter_free(pt.ptc);
216 pt.ptc = NULL;
217out_max:
218 pt.max = 0;
219 pt.fp = NULL;
d86e83b8 220 return ret;
ed60d210 221}