]> git.proxmox.com Git - mirror_zfs.git/commitdiff
libzfs: sendrecv: send_progress_thread: handle SIGINFO/SIGUSR1
authorнаб <nabijaczleweli@nabijaczleweli.xyz>
Tue, 8 Aug 2023 16:35:35 +0000 (18:35 +0200)
committerGitHub <noreply@github.com>
Tue, 8 Aug 2023 16:35:35 +0000 (09:35 -0700)
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

lib/libzfs/Makefile.am
lib/libzfs/libzfs_sendrecv.c
man/man8/zfs-send.8

index cffe341220c2293fe77e58fb06362e0dc1fdccef..5e74d908de3d260eff56470734fce831ba070c7e 100644 (file)
@@ -57,7 +57,7 @@ libzfs_la_LIBADD = \
        libzutil.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
 
index 87a30f54fea824df69ef2abed60e2601592e51f4..e9bc78aa8d3943a8cd71d78c310dd836c592bc30 100644 (file)
@@ -928,6 +928,39 @@ zfs_send_progress(zfs_handle_t *zhp, int fd, uint64_t *bytes_written,
        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 *
 send_progress_thread(void *arg)
 {
@@ -941,6 +974,26 @@ send_progress_thread(void *arg)
        struct tm tm;
        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) {
                (void) fprintf(stderr,
                    "TIME       %s   %sSNAPSHOT %s\n",
@@ -953,12 +1006,12 @@ send_progress_thread(void *arg)
         * Print the progress from ZFS_IOC_SEND_PROGRESS every second.
         */
        for (;;) {
-               (void) sleep(1);
+               pause();
                if ((err = zfs_send_progress(zhp, pa->pa_fd, &bytes,
                    &blocks)) != 0) {
                        if (err == EINTR || err == ENOENT)
-                               return ((void *)0);
-                       return ((void *)(uintptr_t)err);
+                               err = 0;
+                       pthread_exit(((void *)(uintptr_t)err));
                }
 
                (void) time(&t);
@@ -991,21 +1044,25 @@ send_progress_thread(void *arg)
                        (void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n",
                            tm.tm_hour, tm.tm_min, tm.tm_sec,
                            (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));
                        (void) fprintf(stderr, "%02d:%02d:%02d   %5s   %s\n",
                            tm.tm_hour, tm.tm_min, tm.tm_sec,
                            buf, zhp->zfs_name);
                }
        }
+       pthread_cleanup_pop(B_TRUE);
 }
 
 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) pthread_cancel(ptid);
        (void) pthread_join(ptid, &status);
+       pthread_sigmask(SIG_SETMASK, oldmask, NULL);
        int error = (int)(uintptr_t)status;
        if (error != 0 && status != PTHREAD_CANCELED)
                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
                 * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
                 */
-               if (sdd->progress || sdd->progressastitle) {
+               sigset_t oldmask;
+               {
                        pa.pa_zhp = zhp;
                        pa.pa_fd = sdd->outfd;
                        pa.pa_parsable = sdd->parsable;
@@ -1214,13 +1272,13 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
                                zfs_close(zhp);
                                return (err);
                        }
+                       SEND_PROGRESS_THREAD_PARENT_BLOCK(&oldmask);
                }
 
                err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
                    fromorigin, sdd->outfd, flags, sdd->debugnv);
 
-               if ((sdd->progress || sdd->progressastitle) &&
-                   send_progress_thread_exit(zhp->zfs_hdl, tid))
+               if (send_progress_thread_exit(zhp->zfs_hdl, tid, &oldmask))
                        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 };
        int err = 0;
        pthread_t ptid;
+       sigset_t oldmask;
 
-       if (flags->progress || flags->progressastitle) {
+       {
                pa.pa_zhp = zhp;
                pa.pa_fd = fd;
                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,
                            EZFS_THREADCREATEFAILED, errbuf));
                }
+               SEND_PROGRESS_THREAD_PARENT_BLOCK(&oldmask);
        }
 
        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);
        *sizep = size;
 
-       if ((flags->progress || flags->progressastitle) &&
-           send_progress_thread_exit(zhp->zfs_hdl, ptid))
+       if (send_progress_thread_exit(zhp->zfs_hdl, ptid, &oldmask))
                return (-1);
 
        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) {
                progress_arg_t pa = { 0 };
                pthread_t tid;
+               sigset_t oldmask;
                /*
                 * If progress reporting is requested, spawn a new thread to
                 * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
                 */
-               if (flags->progress || flags->progressastitle) {
+               {
                        pa.pa_zhp = zhp;
                        pa.pa_fd = outfd;
                        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);
                                return (error);
                        }
+                       SEND_PROGRESS_THREAD_PARENT_BLOCK(&oldmask);
                }
 
                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)
                        free(redact_book);
 
-               if ((flags->progressastitle || flags->progress) &&
-                   send_progress_thread_exit(hdl, tid)) {
+               if (send_progress_thread_exit(hdl, tid, &oldmask)) {
                        zfs_close(zhp);
                        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
         * ZFS_IOC_SEND_PROGRESS at a regular interval.
         */
-       if (flags->progress || flags->progressastitle) {
+       sigset_t oldmask;
+       {
                pa.pa_zhp = zhp;
                pa.pa_fd = fd;
                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,
                            EZFS_THREADCREATEFAILED, errbuf));
                }
+               SEND_PROGRESS_THREAD_PARENT_BLOCK(&oldmask);
        }
 
        err = lzc_send_redacted(name, from, fd,
            lzc_flags_from_sendflags(flags), redactbook);
 
-       if ((flags->progress || flags->progressastitle) &&
-           send_progress_thread_exit(hdl, ptid))
+       if (send_progress_thread_exit(hdl, ptid, &oldmask))
                        return (-1);
 
        if (err == 0 && (flags->props || flags->holds || flags->backup)) {
index 8cc6ae6ad59b9dd2cdcbaaa417f10a7f46060674..ba604bf778550730f21f0a795453fd6ddb176d37 100644 (file)
@@ -29,7 +29,7 @@
 .\" Copyright 2018 Nexenta Systems, Inc.
 .\" Copyright 2019 Joyent, Inc.
 .\"
-.Dd January 12, 2023
+.Dd July 27, 2023
 .Dt ZFS-SEND 8
 .Os
 .
@@ -297,6 +297,12 @@ This flag can only be used in conjunction with
 .It Fl v , -verbose
 Print verbose information about the stream package generated.
 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
 The format of the stream is committed.
 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
 Print verbose information about the stream package generated.
 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
 .It Xo
 .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,
 because it was removed by the redacted send operation.
 .
+.Sh SIGNALS
+See
+.Fl v .
+.
 .Sh EXAMPLES
 .\" These are, respectively, examples 12, 13 from zfs.8
 .\" Make sure to update them bidirectionally