From b435220922d7cd916f1b7acce313c8174738991c Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Sun, 30 Apr 2023 14:45:45 +1200 Subject: [PATCH 09/11] Use copy_file_range() in pg_upgrade. This gives the kernel the opportunity to copy or clone efficiently. We watch out for EXDEV and fall back to read/write for old Linux kernels. XXX Should we also let the user opt out? --- src/bin/pg_upgrade/file.c | 65 ++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c index d173602882..836b2bbbd0 100644 --- a/src/bin/pg_upgrade/file.c +++ b/src/bin/pg_upgrade/file.c @@ -9,6 +9,7 @@ #include "postgres_fe.h" +#include #include #include #ifdef HAVE_COPYFILE_H @@ -98,32 +99,68 @@ copyFile(const char *src, const char *dst, /* copy in fairly large chunks for best efficiency */ #define COPY_BUF_SIZE (50 * BLCKSZ) +#ifdef HAVE_COPY_FILE_RANGE + buffer = NULL; +#else buffer = (char *) pg_malloc(COPY_BUF_SIZE); +#endif /* perform data copying i.e read src source, write to destination */ while (true) { - ssize_t nbytes = read(src_fd, buffer, COPY_BUF_SIZE); + ssize_t nbytes = 0; - if (nbytes < 0) - pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s", - schemaName, relName, src, strerror(errno)); +#ifdef HAVE_COPY_FILE_RANGE + if (buffer == NULL) + { + nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0); + if (nbytes < 0) + { + if (errno == EXDEV) + { + /* Linux < 5.3 might fail. Fall back to read/write. */ + buffer = (char *) pg_malloc(COPY_BUF_SIZE); + } + else + { + pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s", - if (nbytes == 0) - break; + schemaName, relName, src, strerror(errno)); + } + } + } +#endif - errno = 0; - if (write(dest_fd, buffer, nbytes) != nbytes) + if (buffer) { - /* if write didn't set errno, assume problem is no disk space */ - if (errno == 0) - errno = ENOSPC; - pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %s", - schemaName, relName, dst, strerror(errno)); + nbytes = read(src_fd, buffer, COPY_BUF_SIZE); + + if (nbytes < 0) + pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s", + schemaName, relName, src, strerror(errno)); + if (nbytes > 0) + { + errno = 0; + if (write(dest_fd, buffer, nbytes) != nbytes) + { + /* + * If write didn't set errno, assume problem is no disk + * space. + */ + if (errno == 0) + errno = ENOSPC; + pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %s", + schemaName, relName, dst, strerror(errno)); + } + } } + + if (nbytes == 0) + break; } - pg_free(buffer); + if (buffer) + pg_free(buffer); close(src_fd); close(dest_fd); -- 2.40.1