libzfs: sendrecv: send_progress_thread: handle SIGINFO/SIGUSR1

POSIX timers target the process, not the thread (as does SIGINFO),
so we need to block it in the main thread which will die if interrupted.

Ref: https://101010.pl/@ed1conf@bsd.network/110731819189629373
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
Closes #15113
This commit is contained in:
наб 2023-08-08 18:35:35 +02:00 committed by Brian Behlendorf
parent 1e488eec60
commit 6bdc7259d1
3 changed files with 96 additions and 19 deletions

View File

@ -57,7 +57,7 @@ libzfs_la_LIBADD = \
libzutil.la \ libzutil.la \
libuutil.la libuutil.la
libzfs_la_LIBADD += -lm $(LIBCRYPTO_LIBS) $(ZLIB_LIBS) $(LIBFETCH_LIBS) $(LTLIBINTL) libzfs_la_LIBADD += -lrt -lm $(LIBCRYPTO_LIBS) $(ZLIB_LIBS) $(LIBFETCH_LIBS) $(LTLIBINTL)
libzfs_la_LDFLAGS = -pthread libzfs_la_LDFLAGS = -pthread

View File

@ -928,6 +928,39 @@ zfs_send_progress(zfs_handle_t *zhp, int fd, uint64_t *bytes_written,
return (0); return (0);
} }
static volatile boolean_t send_progress_thread_signal_duetotimer;
static void
send_progress_thread_act(int sig, siginfo_t *info, void *ucontext)
{
(void) sig, (void) ucontext;
send_progress_thread_signal_duetotimer = info->si_code == SI_TIMER;
}
struct timer_desirability {
timer_t timer;
boolean_t desired;
};
static void
timer_delete_cleanup(void *timer)
{
struct timer_desirability *td = timer;
if (td->desired)
timer_delete(td->timer);
}
#ifdef SIGINFO
#define SEND_PROGRESS_THREAD_PARENT_BLOCK_SIGINFO sigaddset(&new, SIGINFO)
#else
#define SEND_PROGRESS_THREAD_PARENT_BLOCK_SIGINFO
#endif
#define SEND_PROGRESS_THREAD_PARENT_BLOCK(old) { \
sigset_t new; \
sigemptyset(&new); \
sigaddset(&new, SIGUSR1); \
SEND_PROGRESS_THREAD_PARENT_BLOCK_SIGINFO; \
pthread_sigmask(SIG_BLOCK, &new, old); \
}
static void * static void *
send_progress_thread(void *arg) send_progress_thread(void *arg)
{ {
@ -941,6 +974,26 @@ send_progress_thread(void *arg)
struct tm tm; struct tm tm;
int err; int err;
const struct sigaction signal_action =
{.sa_sigaction = send_progress_thread_act, .sa_flags = SA_SIGINFO};
struct sigevent timer_cfg =
{.sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGUSR1};
const struct itimerspec timer_time =
{.it_value = {.tv_sec = 1}, .it_interval = {.tv_sec = 1}};
struct timer_desirability timer = {};
sigaction(SIGUSR1, &signal_action, NULL);
#ifdef SIGINFO
sigaction(SIGINFO, &signal_action, NULL);
#endif
if ((timer.desired = pa->pa_progress || pa->pa_astitle)) {
if (timer_create(CLOCK_MONOTONIC, &timer_cfg, &timer.timer))
return ((void *)(uintptr_t)errno);
(void) timer_settime(timer.timer, 0, &timer_time, NULL);
}
pthread_cleanup_push(timer_delete_cleanup, &timer);
if (!pa->pa_parsable && pa->pa_progress) { if (!pa->pa_parsable && pa->pa_progress) {
(void) fprintf(stderr, (void) fprintf(stderr,
"TIME %s %sSNAPSHOT %s\n", "TIME %s %sSNAPSHOT %s\n",
@ -953,12 +1006,12 @@ send_progress_thread(void *arg)
* Print the progress from ZFS_IOC_SEND_PROGRESS every second. * Print the progress from ZFS_IOC_SEND_PROGRESS every second.
*/ */
for (;;) { for (;;) {
(void) sleep(1); pause();
if ((err = zfs_send_progress(zhp, pa->pa_fd, &bytes, if ((err = zfs_send_progress(zhp, pa->pa_fd, &bytes,
&blocks)) != 0) { &blocks)) != 0) {
if (err == EINTR || err == ENOENT) if (err == EINTR || err == ENOENT)
return ((void *)0); err = 0;
return ((void *)(uintptr_t)err); pthread_exit(((void *)(uintptr_t)err));
} }
(void) time(&t); (void) time(&t);
@ -991,21 +1044,25 @@ send_progress_thread(void *arg)
(void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n", (void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n",
tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_hour, tm.tm_min, tm.tm_sec,
(u_longlong_t)bytes, zhp->zfs_name); (u_longlong_t)bytes, zhp->zfs_name);
} else if (pa->pa_progress) { } else if (pa->pa_progress ||
!send_progress_thread_signal_duetotimer) {
zfs_nicebytes(bytes, buf, sizeof (buf)); zfs_nicebytes(bytes, buf, sizeof (buf));
(void) fprintf(stderr, "%02d:%02d:%02d %5s %s\n", (void) fprintf(stderr, "%02d:%02d:%02d %5s %s\n",
tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_hour, tm.tm_min, tm.tm_sec,
buf, zhp->zfs_name); buf, zhp->zfs_name);
} }
} }
pthread_cleanup_pop(B_TRUE);
} }
static boolean_t static boolean_t
send_progress_thread_exit(libzfs_handle_t *hdl, pthread_t ptid) send_progress_thread_exit(
libzfs_handle_t *hdl, pthread_t ptid, sigset_t *oldmask)
{ {
void *status = NULL; void *status = NULL;
(void) pthread_cancel(ptid); (void) pthread_cancel(ptid);
(void) pthread_join(ptid, &status); (void) pthread_join(ptid, &status);
pthread_sigmask(SIG_SETMASK, oldmask, NULL);
int error = (int)(uintptr_t)status; int error = (int)(uintptr_t)status;
if (error != 0 && status != PTHREAD_CANCELED) if (error != 0 && status != PTHREAD_CANCELED)
return (zfs_standard_error(hdl, error, return (zfs_standard_error(hdl, error,
@ -1199,7 +1256,8 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
* If progress reporting is requested, spawn a new thread to * If progress reporting is requested, spawn a new thread to
* poll ZFS_IOC_SEND_PROGRESS at a regular interval. * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
*/ */
if (sdd->progress || sdd->progressastitle) { sigset_t oldmask;
{
pa.pa_zhp = zhp; pa.pa_zhp = zhp;
pa.pa_fd = sdd->outfd; pa.pa_fd = sdd->outfd;
pa.pa_parsable = sdd->parsable; pa.pa_parsable = sdd->parsable;
@ -1214,13 +1272,13 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
zfs_close(zhp); zfs_close(zhp);
return (err); return (err);
} }
SEND_PROGRESS_THREAD_PARENT_BLOCK(&oldmask);
} }
err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj, err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
fromorigin, sdd->outfd, flags, sdd->debugnv); fromorigin, sdd->outfd, flags, sdd->debugnv);
if ((sdd->progress || sdd->progressastitle) && if (send_progress_thread_exit(zhp->zfs_hdl, tid, &oldmask))
send_progress_thread_exit(zhp->zfs_hdl, tid))
return (-1); return (-1);
} }
@ -1562,8 +1620,9 @@ estimate_size(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags,
progress_arg_t pa = { 0 }; progress_arg_t pa = { 0 };
int err = 0; int err = 0;
pthread_t ptid; pthread_t ptid;
sigset_t oldmask;
if (flags->progress || flags->progressastitle) { {
pa.pa_zhp = zhp; pa.pa_zhp = zhp;
pa.pa_fd = fd; pa.pa_fd = fd;
pa.pa_parsable = flags->parsable; pa.pa_parsable = flags->parsable;
@ -1577,6 +1636,7 @@ estimate_size(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags,
return (zfs_error(zhp->zfs_hdl, return (zfs_error(zhp->zfs_hdl,
EZFS_THREADCREATEFAILED, errbuf)); EZFS_THREADCREATEFAILED, errbuf));
} }
SEND_PROGRESS_THREAD_PARENT_BLOCK(&oldmask);
} }
err = lzc_send_space_resume_redacted(zhp->zfs_name, from, err = lzc_send_space_resume_redacted(zhp->zfs_name, from,
@ -1584,8 +1644,7 @@ estimate_size(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags,
redactbook, fd, &size); redactbook, fd, &size);
*sizep = size; *sizep = size;
if ((flags->progress || flags->progressastitle) && if (send_progress_thread_exit(zhp->zfs_hdl, ptid, &oldmask))
send_progress_thread_exit(zhp->zfs_hdl, ptid))
return (-1); return (-1);
if (!flags->progress && !flags->parsable) if (!flags->progress && !flags->parsable)
@ -1876,11 +1935,12 @@ zfs_send_resume_impl_cb_impl(libzfs_handle_t *hdl, sendflags_t *flags,
if (!flags->dryrun) { if (!flags->dryrun) {
progress_arg_t pa = { 0 }; progress_arg_t pa = { 0 };
pthread_t tid; pthread_t tid;
sigset_t oldmask;
/* /*
* If progress reporting is requested, spawn a new thread to * If progress reporting is requested, spawn a new thread to
* poll ZFS_IOC_SEND_PROGRESS at a regular interval. * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
*/ */
if (flags->progress || flags->progressastitle) { {
pa.pa_zhp = zhp; pa.pa_zhp = zhp;
pa.pa_fd = outfd; pa.pa_fd = outfd;
pa.pa_parsable = flags->parsable; pa.pa_parsable = flags->parsable;
@ -1898,6 +1958,7 @@ zfs_send_resume_impl_cb_impl(libzfs_handle_t *hdl, sendflags_t *flags,
zfs_close(zhp); zfs_close(zhp);
return (error); return (error);
} }
SEND_PROGRESS_THREAD_PARENT_BLOCK(&oldmask);
} }
error = lzc_send_resume_redacted(zhp->zfs_name, fromname, outfd, error = lzc_send_resume_redacted(zhp->zfs_name, fromname, outfd,
@ -1905,8 +1966,7 @@ zfs_send_resume_impl_cb_impl(libzfs_handle_t *hdl, sendflags_t *flags,
if (redact_book != NULL) if (redact_book != NULL)
free(redact_book); free(redact_book);
if ((flags->progressastitle || flags->progress) && if (send_progress_thread_exit(hdl, tid, &oldmask)) {
send_progress_thread_exit(hdl, tid)) {
zfs_close(zhp); zfs_close(zhp);
return (-1); return (-1);
} }
@ -2691,7 +2751,8 @@ zfs_send_one_cb_impl(zfs_handle_t *zhp, const char *from, int fd,
* If progress reporting is requested, spawn a new thread to poll * If progress reporting is requested, spawn a new thread to poll
* ZFS_IOC_SEND_PROGRESS at a regular interval. * ZFS_IOC_SEND_PROGRESS at a regular interval.
*/ */
if (flags->progress || flags->progressastitle) { sigset_t oldmask;
{
pa.pa_zhp = zhp; pa.pa_zhp = zhp;
pa.pa_fd = fd; pa.pa_fd = fd;
pa.pa_parsable = flags->parsable; pa.pa_parsable = flags->parsable;
@ -2708,13 +2769,13 @@ zfs_send_one_cb_impl(zfs_handle_t *zhp, const char *from, int fd,
return (zfs_error(zhp->zfs_hdl, return (zfs_error(zhp->zfs_hdl,
EZFS_THREADCREATEFAILED, errbuf)); EZFS_THREADCREATEFAILED, errbuf));
} }
SEND_PROGRESS_THREAD_PARENT_BLOCK(&oldmask);
} }
err = lzc_send_redacted(name, from, fd, err = lzc_send_redacted(name, from, fd,
lzc_flags_from_sendflags(flags), redactbook); lzc_flags_from_sendflags(flags), redactbook);
if ((flags->progress || flags->progressastitle) && if (send_progress_thread_exit(hdl, ptid, &oldmask))
send_progress_thread_exit(hdl, ptid))
return (-1); return (-1);
if (err == 0 && (flags->props || flags->holds || flags->backup)) { if (err == 0 && (flags->props || flags->holds || flags->backup)) {

View File

@ -29,7 +29,7 @@
.\" Copyright 2018 Nexenta Systems, Inc. .\" Copyright 2018 Nexenta Systems, Inc.
.\" Copyright 2019 Joyent, Inc. .\" Copyright 2019 Joyent, Inc.
.\" .\"
.Dd January 12, 2023 .Dd July 27, 2023
.Dt ZFS-SEND 8 .Dt ZFS-SEND 8
.Os .Os
. .
@ -297,6 +297,12 @@ This flag can only be used in conjunction with
.It Fl v , -verbose .It Fl v , -verbose
Print verbose information about the stream package generated. Print verbose information about the stream package generated.
This information includes a per-second report of how much data has been sent. This information includes a per-second report of how much data has been sent.
The same report can be requested by sending
.Dv SIGINFO
or
.Dv SIGUSR1 ,
regardless of
.Fl v .
.Pp .Pp
The format of the stream is committed. The format of the stream is committed.
You will be able to receive your streams on future versions of ZFS. You will be able to receive your streams on future versions of ZFS.
@ -433,6 +439,12 @@ and the verbose output goes to standard error
.It Fl v , -verbose .It Fl v , -verbose
Print verbose information about the stream package generated. Print verbose information about the stream package generated.
This information includes a per-second report of how much data has been sent. This information includes a per-second report of how much data has been sent.
The same report can be requested by sending
.Dv SIGINFO
or
.Dv SIGUSR1 ,
regardless of
.Fl v .
.El .El
.It Xo .It Xo
.Nm zfs .Nm zfs
@ -669,6 +681,10 @@ ones on the source, and are ready to be used, while the parent snapshot on the
target contains none of the username and password data present on the source, target contains none of the username and password data present on the source,
because it was removed by the redacted send operation. because it was removed by the redacted send operation.
. .
.Sh SIGNALS
See
.Fl v .
.
.Sh EXAMPLES .Sh EXAMPLES
.\" These are, respectively, examples 12, 13 from zfs.8 .\" These are, respectively, examples 12, 13 from zfs.8
.\" Make sure to update them bidirectionally .\" Make sure to update them bidirectionally