Skip to content

Commit 033f8fd

Browse files
committed
Refactored lzc_send_resume_redacted with a thread.
After the changes documented in #11445, writing to certain files errored out with an opaque error message. Unfortunately, the path of least resistance to fixing this seemed to be sticking a pipe into the path of zfs send, along with a thread to copy data to stdout. Signed-off-by: Rich Ercolani <[email protected]>
1 parent e5e76bd commit 033f8fd

File tree

6 files changed

+150
-68
lines changed

6 files changed

+150
-68
lines changed

lib/libzfs/Makefile.am

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ if BUILD_LINUX
4343
USER_C += \
4444
os/linux/libzfs_mount_os.c \
4545
os/linux/libzfs_pool_os.c \
46-
os/linux/libzfs_sendrecv_os.c \
4746
os/linux/libzfs_util_os.c
4847
endif
4948

lib/libzfs/libzfs_impl.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,6 @@ extern int libzfs_load_module(void);
256256
extern int zpool_relabel_disk(libzfs_handle_t *hdl, const char *path,
257257
const char *msg);
258258
extern int find_shares_object(differ_info_t *di);
259-
extern void libzfs_set_pipe_max(int infd);
260259
extern void zfs_commit_proto(zfs_share_proto_t *);
261260

262261
#ifdef __cplusplus

lib/libzfs/libzfs_sendrecv.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5142,13 +5142,6 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
51425142
return (-2);
51435143
}
51445144

5145-
/*
5146-
* It is not uncommon for gigabytes to be processed in zfs receive.
5147-
* Speculatively increase the buffer size if supported by the platform.
5148-
*/
5149-
if (S_ISFIFO(sb.st_mode))
5150-
libzfs_set_pipe_max(infd);
5151-
51525145
if (props) {
51535146
err = nvlist_lookup_string(props, "origin", &originsnap);
51545147
if (err && err != ENOENT)

lib/libzfs/os/freebsd/libzfs_compat.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,6 @@
3939
#define ZFS_KMOD "openzfs"
4040
#endif
4141

42-
void
43-
libzfs_set_pipe_max(int infd)
44-
{
45-
/* FreeBSD automatically resizes */
46-
}
47-
4842
static int
4943
execvPe(const char *name, const char *path, char * const *argv,
5044
char * const *envp)

lib/libzfs/os/linux/libzfs_sendrecv_os.c

Lines changed: 0 additions & 52 deletions
This file was deleted.

lib/libzfs_core/libzfs_core.c

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* Copyright 2017 RackTop Systems.
2727
* Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
2828
* Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved.
29+
* Copyright (c) 2021 Rich Ercolani.
2930
*/
3031

3132
/*
@@ -96,6 +97,19 @@ static int g_fd = -1;
9697
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
9798
static int g_refcount;
9899

100+
#ifdef __linux__
101+
#ifndef F_SETPIPE_SZ
102+
#define F_SETPIPE_SZ (F_SETLEASE + 7)
103+
#endif /* F_SETPIPE_SZ */
104+
105+
#ifndef F_GETPIPE_SZ
106+
#define F_GETPIPE_SZ (F_GETLEASE + 7)
107+
#endif /* F_GETPIPE_SZ */
108+
#endif
109+
110+
static unsigned long lzc_get_pipe_max(void);
111+
static void lzc_set_pipe_max(int infd);
112+
99113
#ifdef ZFS_DEBUG
100114
static zfs_ioc_t fail_ioc_cmd = ZFS_IOC_LAST;
101115
static zfs_errno_t fail_ioc_err;
@@ -645,6 +659,92 @@ lzc_send_resume(const char *snapname, const char *from, int fd,
645659
resumeoff, NULL));
646660
}
647661

662+
static unsigned long
663+
lzc_get_pipe_max()
664+
{
665+
/* FreeBSD automatically grows to 64k */
666+
unsigned long max_psize = 65536;
667+
#ifdef __linux__
668+
FILE *procf = fopen("/proc/sys/fs/pipe-max-size", "re");
669+
670+
if (procf != NULL) {
671+
if (fscanf(procf, "%lu", &max_psize) <= 0) {
672+
max_psize = max_psize;
673+
}
674+
fclose(procf);
675+
}
676+
#endif
677+
return (max_psize);
678+
}
679+
680+
static void
681+
lzc_set_pipe_max(int infd)
682+
{
683+
#ifdef __linux__
684+
unsigned long max_psize = lzc_get_pipe_max();
685+
long cur_psize;
686+
cur_psize = fcntl(infd, F_GETPIPE_SZ);
687+
if (cur_psize > 0 &&
688+
max_psize > (unsigned long) cur_psize)
689+
fcntl(infd, F_SETPIPE_SZ,
690+
max_psize);
691+
#endif
692+
}
693+
694+
695+
struct sendargs {
696+
int ioctlfd;
697+
int inputfd;
698+
int outputfd;
699+
};
700+
typedef struct sendargs sendargs_t;
701+
702+
static void *
703+
do_send_output(void *voidargs)
704+
{
705+
sendargs_t *args = (sendargs_t *)voidargs;
706+
sigset_t sigs;
707+
int buflen = lzc_get_pipe_max();
708+
709+
/*
710+
* See the comment above the close() call for why
711+
* we can't just die from SIGPIPE.
712+
*/
713+
sigemptyset(&sigs);
714+
sigaddset(&sigs, SIGPIPE);
715+
pthread_sigmask(SIG_BLOCK, &sigs, NULL);
716+
717+
718+
int err = 1;
719+
#ifdef __linux__
720+
while (err > 0) {
721+
err = splice(args->inputfd, NULL, args->outputfd, NULL, buflen,
722+
SPLICE_F_MORE);
723+
}
724+
#else
725+
void* buf = calloc(1, buflen);
726+
while (err > 0) {
727+
err = read(args->inputfd, buf, buflen);
728+
if (err <= 0) {
729+
break;
730+
}
731+
err = write(args->outputfd, buf, err);
732+
}
733+
free(buf);
734+
#endif
735+
if (err < 0) {
736+
err = errno;
737+
}
738+
/*
739+
* If we just return here, the other thread often blocks
740+
* indefinitely on the ioctl completing, which won't happen
741+
* because we stopped consuming the data. So we close the pipe
742+
* here, and the other thread exits in a timely fashion.
743+
*/
744+
close(args->inputfd);
745+
return ((void *)(uintptr_t)err);
746+
}
747+
648748
/*
649749
* snapname: The name of the "tosnap", or the snapshot whose contents we are
650750
* sending.
@@ -664,9 +764,18 @@ lzc_send_resume_redacted(const char *snapname, const char *from, int fd,
664764
{
665765
nvlist_t *args;
666766
int err;
767+
int pipefd[2];
768+
pthread_t mythread;
769+
sendargs_t sendargs;
770+
int threadstatus;
771+
772+
773+
err = pipe2(pipefd, O_CLOEXEC);
774+
775+
lzc_set_pipe_max(pipefd[0]);
667776

668777
args = fnvlist_alloc();
669-
fnvlist_add_int32(args, "fd", fd);
778+
fnvlist_add_int32(args, "fd", pipefd[1]);
670779
if (from != NULL)
671780
fnvlist_add_string(args, "fromsnap", from);
672781
if (flags & LZC_SEND_FLAG_LARGE_BLOCK)
@@ -686,8 +795,32 @@ lzc_send_resume_redacted(const char *snapname, const char *from, int fd,
686795
if (redactbook != NULL)
687796
fnvlist_add_string(args, "redactbook", redactbook);
688797

798+
sendargs.inputfd = pipefd[0];
799+
sendargs.outputfd = fd;
800+
sendargs.ioctlfd = pipefd[1];
801+
802+
pthread_create(&mythread, NULL, do_send_output, (void *)&sendargs);
803+
689804
err = lzc_ioctl(ZFS_IOC_SEND_NEW, snapname, args, NULL);
805+
806+
close(pipefd[1]);
807+
808+
pthread_join(mythread, (void *)&threadstatus);
809+
690810
nvlist_free(args);
811+
812+
813+
if (threadstatus != 0) {
814+
err = threadstatus;
815+
/*
816+
* if we don't set errno here, there are some edge cases
817+
* where we wind up dying unexpectedly with
818+
* "internal error: [normal warning msg]: Success"
819+
*/
820+
errno = threadstatus;
821+
}
822+
823+
691824
return (err);
692825
}
693826

@@ -792,6 +925,7 @@ recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops,
792925
char *atp;
793926
int error;
794927
boolean_t payload = B_FALSE;
928+
struct stat sb;
795929

796930
ASSERT3S(g_refcount, >, 0);
797931
VERIFY3S(g_fd, !=, -1);
@@ -811,6 +945,21 @@ recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops,
811945
*slashp = '\0';
812946
}
813947

948+
/*
949+
* The only way fstat can fail is if we do not have a valid file
950+
* descriptor.
951+
*/
952+
if (fstat(input_fd, &sb) == -1) {
953+
return (-errno);
954+
}
955+
956+
/*
957+
* It is not uncommon for gigabytes to be processed in zfs receive.
958+
* Speculatively increase the buffer size if supported by the platform.
959+
*/
960+
if (S_ISFIFO(sb.st_mode))
961+
lzc_set_pipe_max(input_fd);
962+
814963
/*
815964
* The begin_record is normally a non-byteswapped BEGIN record.
816965
* For resumable streams it may be set to any non-byteswapped

0 commit comments

Comments
 (0)