mirror of
https://github.com/git/git.git
synced 2026-08-09 01:21:47 +00:00
Merge branch 'ps/writev' into jch
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) wrapper
This commit is contained in:
4
Makefile
4
Makefile
@@ -2045,6 +2045,10 @@ ifdef NO_PREAD
|
||||
COMPAT_CFLAGS += -DNO_PREAD
|
||||
COMPAT_OBJS += compat/pread.o
|
||||
endif
|
||||
ifdef NO_WRITEV
|
||||
COMPAT_CFLAGS += -DNO_WRITEV
|
||||
COMPAT_OBJS += compat/writev.o
|
||||
endif
|
||||
ifdef NO_FAST_WORKING_DIRECTORY
|
||||
BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
|
||||
endif
|
||||
|
||||
@@ -3335,6 +3335,7 @@ static void cat_blob_write(const char *buf, unsigned long size)
|
||||
static void cat_blob(struct object_entry *oe, struct object_id *oid)
|
||||
{
|
||||
struct strbuf line = STRBUF_INIT;
|
||||
struct iovec iov[3];
|
||||
unsigned long size;
|
||||
enum object_type type = 0;
|
||||
char *buf;
|
||||
@@ -3368,10 +3369,21 @@ static void cat_blob(struct object_entry *oe, struct object_id *oid)
|
||||
strbuf_reset(&line);
|
||||
strbuf_addf(&line, "%s %s %"PRIuMAX"\n", oid_to_hex(oid),
|
||||
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);
|
||||
cat_blob_write(buf, size);
|
||||
cat_blob_write("\n", 1);
|
||||
if (oe && oe->pack_id == pack_id) {
|
||||
last_blob.offset = oe->idx.offset;
|
||||
strbuf_attach(&last_blob.data, buf, size, size + 1);
|
||||
|
||||
@@ -148,6 +148,9 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/statvfs.h>
|
||||
#ifndef NO_WRITEV
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
#include <termios.h>
|
||||
#ifndef NO_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);
|
||||
#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
|
||||
#define setenv gitsetenv
|
||||
int gitsetenv(const char *, const char *, int);
|
||||
|
||||
41
compat/writev.c
Normal file
41
compat/writev.c
Normal file
@@ -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)
|
||||
HAVE_ALLOCA_H = YesPlease
|
||||
NO_PREAD = YesPlease
|
||||
NO_WRITEV = YesPlease
|
||||
NEEDS_CRYPTO_WITH_SSL = YesPlease
|
||||
NO_LIBGEN_H = YesPlease
|
||||
NO_POLL = YesPlease
|
||||
@@ -697,6 +698,7 @@ ifeq ($(uname_S),MINGW)
|
||||
pathsep = ;
|
||||
HAVE_ALLOCA_H = YesPlease
|
||||
NO_PREAD = YesPlease
|
||||
NO_WRITEV = YesPlease
|
||||
NEEDS_CRYPTO_WITH_SSL = YesPlease
|
||||
NO_LIBGEN_H = YesPlease
|
||||
NO_POLL = YesPlease
|
||||
|
||||
@@ -378,7 +378,7 @@ endif()
|
||||
#function checks
|
||||
set(function_checks
|
||||
strcasestr memmem strlcpy strtoimax strtoumax strtoull
|
||||
setenv mkdtemp poll pread memmem)
|
||||
setenv mkdtemp poll pread memmem writev)
|
||||
|
||||
#unsetenv,hstrerror are incompatible with windows build
|
||||
if(NOT WIN32)
|
||||
@@ -423,6 +423,10 @@ if(NOT HAVE_MEMMEM)
|
||||
list(APPEND compat_SOURCES compat/memmem.c)
|
||||
endif()
|
||||
|
||||
if(NOT HAVE_WRITEV)
|
||||
list(APPEND compat_SOURCES compat/writev.c)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
if(NOT HAVE_UNSETENV)
|
||||
list(APPEND compat_SOURCES compat/unsetenv.c)
|
||||
|
||||
@@ -1464,6 +1464,7 @@ checkfuncs = {
|
||||
'initgroups' : [],
|
||||
'strtoumax' : ['strtoumax.c', 'strtoimax.c'],
|
||||
'pread' : ['pread.c'],
|
||||
'writev' : ['writev.c'],
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
while (sz) {
|
||||
struct iovec iov[2];
|
||||
unsigned n;
|
||||
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) {
|
||||
xsnprintf(hdr, sizeof(hdr), "%04x", n + 5);
|
||||
hdr[4] = band;
|
||||
write_or_die(fd, hdr, 5);
|
||||
iov[0].iov_base = hdr;
|
||||
iov[0].iov_len = 5;
|
||||
} else {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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, ...);
|
||||
ssize_t xread(int fd, 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);
|
||||
int xdup(int fd);
|
||||
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 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 fflush_or_die(FILE *f);
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user