Merge branch 'ps/writev'
A compatibility wrapper for writev(3p) has been reintroduced, including fixes for CMake build and 'MAX_IO_SIZE' limits on NonStop. Calls to write(3p) in send_sideband() and cat_blob() have been refactored to use writev(3p) wrappers to reduce syscall overhead. * ps/writev: fast-import: use writev(3p) to send cat-blob responses sideband: use writev(3p) to send pktlines wrapper: properly handle MAX_IO_SIZE in writev(3p) wrapper: introduce writev(3p) wrappers compat/posix: introduce writev(3p) wrappermain
commit
3beb8bb742
4
Makefile
4
Makefile
|
|
@ -2045,6 +2045,10 @@ ifdef NO_PREAD
|
||||||
COMPAT_CFLAGS += -DNO_PREAD
|
COMPAT_CFLAGS += -DNO_PREAD
|
||||||
COMPAT_OBJS += compat/pread.o
|
COMPAT_OBJS += compat/pread.o
|
||||||
endif
|
endif
|
||||||
|
ifdef NO_WRITEV
|
||||||
|
COMPAT_CFLAGS += -DNO_WRITEV
|
||||||
|
COMPAT_OBJS += compat/writev.o
|
||||||
|
endif
|
||||||
ifdef NO_FAST_WORKING_DIRECTORY
|
ifdef NO_FAST_WORKING_DIRECTORY
|
||||||
BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
|
BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
|
||||||
endif
|
endif
|
||||||
|
|
|
||||||
|
|
@ -3333,6 +3333,7 @@ static void cat_blob_write(const char *buf, unsigned long size)
|
||||||
static void cat_blob(struct object_entry *oe, struct object_id *oid)
|
static void cat_blob(struct object_entry *oe, struct object_id *oid)
|
||||||
{
|
{
|
||||||
struct strbuf line = STRBUF_INIT;
|
struct strbuf line = STRBUF_INIT;
|
||||||
|
struct iovec iov[3];
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
enum object_type type = 0;
|
enum object_type type = 0;
|
||||||
char *buf;
|
char *buf;
|
||||||
|
|
@ -3366,10 +3367,21 @@ static void cat_blob(struct object_entry *oe, struct object_id *oid)
|
||||||
strbuf_reset(&line);
|
strbuf_reset(&line);
|
||||||
strbuf_addf(&line, "%s %s %"PRIuMAX"\n", oid_to_hex(oid),
|
strbuf_addf(&line, "%s %s %"PRIuMAX"\n", oid_to_hex(oid),
|
||||||
type_name(type), (uintmax_t)size);
|
type_name(type), (uintmax_t)size);
|
||||||
cat_blob_write(line.buf, line.len);
|
|
||||||
|
/*
|
||||||
|
* Write the header, the payload and the trailing newline with a
|
||||||
|
* single writev(3p) call instead of three separate write(3p) calls.
|
||||||
|
*/
|
||||||
|
iov[0].iov_base = line.buf;
|
||||||
|
iov[0].iov_len = line.len;
|
||||||
|
iov[1].iov_base = buf;
|
||||||
|
iov[1].iov_len = size;
|
||||||
|
iov[2].iov_base = (void *) "\n";
|
||||||
|
iov[2].iov_len = 1;
|
||||||
|
|
||||||
|
if (writev_in_full(cat_blob_fd, iov, ARRAY_SIZE(iov)) < 0)
|
||||||
|
die_errno(_("write to frontend failed"));
|
||||||
strbuf_release(&line);
|
strbuf_release(&line);
|
||||||
cat_blob_write(buf, size);
|
|
||||||
cat_blob_write("\n", 1);
|
|
||||||
if (oe && oe->pack_id == pack_id) {
|
if (oe && oe->pack_id == pack_id) {
|
||||||
last_blob.offset = oe->idx.offset;
|
last_blob.offset = oe->idx.offset;
|
||||||
strbuf_attach(&last_blob.data, buf, size, size + 1);
|
strbuf_attach(&last_blob.data, buf, size, size + 1);
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,9 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
|
#ifndef NO_WRITEV
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#endif
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#ifndef NO_SYS_SELECT_H
|
#ifndef NO_SYS_SELECT_H
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
|
|
@ -334,6 +337,17 @@ int git_lstat(const char *, struct stat *);
|
||||||
ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
|
ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef NO_WRITEV
|
||||||
|
#define writev git_writev
|
||||||
|
#define iovec git_iovec
|
||||||
|
struct git_iovec {
|
||||||
|
void *iov_base;
|
||||||
|
size_t iov_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NO_SETENV
|
#ifdef NO_SETENV
|
||||||
#define setenv gitsetenv
|
#define setenv gitsetenv
|
||||||
int gitsetenv(const char *, const char *, int);
|
int gitsetenv(const char *, const char *, int);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
#include "../git-compat-util.h"
|
||||||
|
#include "../wrapper.h"
|
||||||
|
|
||||||
|
ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt)
|
||||||
|
{
|
||||||
|
size_t sum = 0;
|
||||||
|
|
||||||
|
if (iovcnt <= 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* According to writev(3p), the syscall shall error with EINVAL in case
|
||||||
|
* the sum of `iov_len` overflows `ssize_t`.
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
if (iov[i].iov_len > maximum_signed_value_of_type(ssize_t) ||
|
||||||
|
unsigned_add_overflows(iov[i].iov_len, sum) ||
|
||||||
|
iov[i].iov_len + sum > maximum_signed_value_of_type(ssize_t)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum += iov[i].iov_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We only ever write the first non-empty vector so that we can
|
||||||
|
* guarantee the call to be non-interleaving as guaranteed by POSIX.
|
||||||
|
* This works just fine as callers have to loop around writev anyway.
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
if (!iov[i].iov_len)
|
||||||
|
continue;
|
||||||
|
return xwrite(fd, iov[i].iov_base, iov[i].iov_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When all iovec members were zero we ought to return 0 according to POSIX. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -483,6 +483,7 @@ ifeq ($(uname_S),Windows)
|
||||||
SANE_TOOL_PATH ?= $(msvc_bin_dir_msys)
|
SANE_TOOL_PATH ?= $(msvc_bin_dir_msys)
|
||||||
HAVE_ALLOCA_H = YesPlease
|
HAVE_ALLOCA_H = YesPlease
|
||||||
NO_PREAD = YesPlease
|
NO_PREAD = YesPlease
|
||||||
|
NO_WRITEV = YesPlease
|
||||||
NEEDS_CRYPTO_WITH_SSL = YesPlease
|
NEEDS_CRYPTO_WITH_SSL = YesPlease
|
||||||
NO_LIBGEN_H = YesPlease
|
NO_LIBGEN_H = YesPlease
|
||||||
NO_POLL = YesPlease
|
NO_POLL = YesPlease
|
||||||
|
|
@ -697,6 +698,7 @@ ifeq ($(uname_S),MINGW)
|
||||||
pathsep = ;
|
pathsep = ;
|
||||||
HAVE_ALLOCA_H = YesPlease
|
HAVE_ALLOCA_H = YesPlease
|
||||||
NO_PREAD = YesPlease
|
NO_PREAD = YesPlease
|
||||||
|
NO_WRITEV = YesPlease
|
||||||
NEEDS_CRYPTO_WITH_SSL = YesPlease
|
NEEDS_CRYPTO_WITH_SSL = YesPlease
|
||||||
NO_LIBGEN_H = YesPlease
|
NO_LIBGEN_H = YesPlease
|
||||||
NO_POLL = YesPlease
|
NO_POLL = YesPlease
|
||||||
|
|
|
||||||
|
|
@ -378,7 +378,7 @@ endif()
|
||||||
#function checks
|
#function checks
|
||||||
set(function_checks
|
set(function_checks
|
||||||
strcasestr memmem strlcpy strtoimax strtoumax strtoull
|
strcasestr memmem strlcpy strtoimax strtoumax strtoull
|
||||||
setenv mkdtemp poll pread memmem)
|
setenv mkdtemp poll pread memmem writev)
|
||||||
|
|
||||||
#unsetenv,hstrerror are incompatible with windows build
|
#unsetenv,hstrerror are incompatible with windows build
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
|
|
@ -423,6 +423,10 @@ if(NOT HAVE_MEMMEM)
|
||||||
list(APPEND compat_SOURCES compat/memmem.c)
|
list(APPEND compat_SOURCES compat/memmem.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT HAVE_WRITEV)
|
||||||
|
list(APPEND compat_SOURCES compat/writev.c)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
if(NOT HAVE_UNSETENV)
|
if(NOT HAVE_UNSETENV)
|
||||||
list(APPEND compat_SOURCES compat/unsetenv.c)
|
list(APPEND compat_SOURCES compat/unsetenv.c)
|
||||||
|
|
|
||||||
|
|
@ -1464,6 +1464,7 @@ checkfuncs = {
|
||||||
'initgroups' : [],
|
'initgroups' : [],
|
||||||
'strtoumax' : ['strtoumax.c', 'strtoimax.c'],
|
'strtoumax' : ['strtoumax.c', 'strtoimax.c'],
|
||||||
'pread' : ['pread.c'],
|
'pread' : ['pread.c'],
|
||||||
|
'writev' : ['writev.c'],
|
||||||
}
|
}
|
||||||
|
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
|
|
|
||||||
14
sideband.c
14
sideband.c
|
|
@ -445,6 +445,7 @@ void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_ma
|
||||||
const char *p = data;
|
const char *p = data;
|
||||||
|
|
||||||
while (sz) {
|
while (sz) {
|
||||||
|
struct iovec iov[2];
|
||||||
unsigned n;
|
unsigned n;
|
||||||
char hdr[5];
|
char hdr[5];
|
||||||
|
|
||||||
|
|
@ -454,12 +455,19 @@ void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_ma
|
||||||
if (0 <= band) {
|
if (0 <= band) {
|
||||||
xsnprintf(hdr, sizeof(hdr), "%04x", n + 5);
|
xsnprintf(hdr, sizeof(hdr), "%04x", n + 5);
|
||||||
hdr[4] = band;
|
hdr[4] = band;
|
||||||
write_or_die(fd, hdr, 5);
|
iov[0].iov_base = hdr;
|
||||||
|
iov[0].iov_len = 5;
|
||||||
} else {
|
} else {
|
||||||
xsnprintf(hdr, sizeof(hdr), "%04x", n + 4);
|
xsnprintf(hdr, sizeof(hdr), "%04x", n + 4);
|
||||||
write_or_die(fd, hdr, 4);
|
iov[0].iov_base = hdr;
|
||||||
|
iov[0].iov_len = 4;
|
||||||
}
|
}
|
||||||
write_or_die(fd, p, n);
|
|
||||||
|
iov[1].iov_base = (void *) p;
|
||||||
|
iov[1].iov_len = n;
|
||||||
|
|
||||||
|
writev_or_die(fd, iov, ARRAY_SIZE(iov));
|
||||||
|
|
||||||
p += n;
|
p += n;
|
||||||
sz -= n;
|
sz -= n;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
78
wrapper.c
78
wrapper.c
|
|
@ -323,6 +323,84 @@ ssize_t write_in_full(int fd, const void *buf, size_t count)
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t xwritev(int fd, struct iovec *iov, int iovcnt)
|
||||||
|
{
|
||||||
|
size_t allowed = MAX_IO_SIZE;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some platforms define a comparatively small `MAX_IO_SIZE` that
|
||||||
|
* limits how many bytes can be written with a single call to
|
||||||
|
* write(3p) or writev(3p); exceeding that limit causes the syscall to
|
||||||
|
* fail with EINVAL. Just like xwrite() chomps overly large requests
|
||||||
|
* for write(3p), pretend that the underlying writev(3p) performed a
|
||||||
|
* short write by only passing along the leading iovec entries that
|
||||||
|
* fit into that limit.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < iovcnt; i++) {
|
||||||
|
if (iov[i].iov_len > allowed) {
|
||||||
|
/*
|
||||||
|
* If the first buffer is larger than MAX_IO_SIZE,
|
||||||
|
* let xwrite() deal with it.
|
||||||
|
*/
|
||||||
|
if (!i)
|
||||||
|
return xwrite(fd, iov->iov_base, iov->iov_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
allowed -= iov[i].iov_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
ssize_t bytes_written = writev(fd, iov, i);
|
||||||
|
if (bytes_written < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
if (handle_nonblock(fd, POLLOUT, errno))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_written;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t writev_in_full(int fd, struct iovec *iov, int iovcnt)
|
||||||
|
{
|
||||||
|
ssize_t total_written = 0;
|
||||||
|
|
||||||
|
while (iovcnt) {
|
||||||
|
ssize_t bytes_written = xwritev(fd, iov, iovcnt);
|
||||||
|
if (bytes_written < 0)
|
||||||
|
return -1;
|
||||||
|
if (!bytes_written) {
|
||||||
|
errno = ENOSPC;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_written += bytes_written;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We first need to discard any iovec entities that have been
|
||||||
|
* fully written.
|
||||||
|
*/
|
||||||
|
while (iovcnt && (size_t)bytes_written >= iov->iov_len) {
|
||||||
|
bytes_written -= iov->iov_len;
|
||||||
|
iov++;
|
||||||
|
iovcnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finally, we need to adjust the last iovec in case we have
|
||||||
|
* performed a partial write.
|
||||||
|
*/
|
||||||
|
if (iovcnt && bytes_written) {
|
||||||
|
iov->iov_base = (char *) iov->iov_base + bytes_written;
|
||||||
|
iov->iov_len -= bytes_written;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total_written;
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset)
|
ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset)
|
||||||
{
|
{
|
||||||
char *p = buf;
|
char *p = buf;
|
||||||
|
|
|
||||||
10
wrapper.h
10
wrapper.h
|
|
@ -16,6 +16,7 @@ void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_
|
||||||
int xopen(const char *path, int flags, ...);
|
int xopen(const char *path, int flags, ...);
|
||||||
ssize_t xread(int fd, void *buf, size_t len);
|
ssize_t xread(int fd, void *buf, size_t len);
|
||||||
ssize_t xwrite(int fd, const void *buf, size_t len);
|
ssize_t xwrite(int fd, const void *buf, size_t len);
|
||||||
|
ssize_t xwritev(int fd, struct iovec *iov, int iovcnt);
|
||||||
ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
|
ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
|
||||||
int xdup(int fd);
|
int xdup(int fd);
|
||||||
FILE *xfopen(const char *path, const char *mode);
|
FILE *xfopen(const char *path, const char *mode);
|
||||||
|
|
@ -47,6 +48,15 @@ ssize_t read_in_full(int fd, void *buf, size_t count);
|
||||||
ssize_t write_in_full(int fd, const void *buf, size_t count);
|
ssize_t write_in_full(int fd, const void *buf, size_t count);
|
||||||
ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
|
ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to write all iovecs. Returns -1 in case an error occurred with a proper
|
||||||
|
* errno set, the number of bytes written otherwise.
|
||||||
|
*
|
||||||
|
* Note that the iovec will be modified as a result of this call to adjust for
|
||||||
|
* partial writes!
|
||||||
|
*/
|
||||||
|
ssize_t writev_in_full(int fd, struct iovec *iov, int iovcnt);
|
||||||
|
|
||||||
static inline ssize_t write_str_in_full(int fd, const char *str)
|
static inline ssize_t write_str_in_full(int fd, const char *str)
|
||||||
{
|
{
|
||||||
return write_in_full(fd, str, strlen(str));
|
return write_in_full(fd, str, strlen(str));
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,14 @@ void write_or_die(int fd, const void *buf, size_t count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void writev_or_die(int fd, struct iovec *iov, int iovlen)
|
||||||
|
{
|
||||||
|
if (writev_in_full(fd, iov, iovlen) < 0) {
|
||||||
|
check_pipe(errno);
|
||||||
|
die_errno("writev error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void fwrite_or_die(FILE *f, const void *buf, size_t count)
|
void fwrite_or_die(FILE *f, const void *buf, size_t count)
|
||||||
{
|
{
|
||||||
if (fwrite(buf, 1, count, f) != count)
|
if (fwrite(buf, 1, count, f) != count)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ void fprintf_or_die(FILE *, const char *fmt, ...);
|
||||||
void fwrite_or_die(FILE *f, const void *buf, size_t count);
|
void fwrite_or_die(FILE *f, const void *buf, size_t count);
|
||||||
void fflush_or_die(FILE *f);
|
void fflush_or_die(FILE *f);
|
||||||
void write_or_die(int fd, const void *buf, size_t count);
|
void write_or_die(int fd, const void *buf, size_t count);
|
||||||
|
void writev_or_die(int fd, struct iovec *iov, int iovlen);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These values are used to help identify parts of a repository to fsync.
|
* These values are used to help identify parts of a repository to fsync.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue