aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2007-02-19 16:04:03 +0000
committerMiklos Szeredi <miklos@szeredi.hu>2007-02-19 16:04:03 +0000
commitfeeaf763404fe7797b1daf2b0c009f35e8db1f9d (patch)
treefd2401a50ba0108cb9c3888b88c6eb0d54a63506
parentb3ffcfe3ac81b96026e9af04fdbb64e1efa5fb2f (diff)
downloadsshfs-feeaf763404fe7797b1daf2b0c009f35e8db1f9d.tar
sshfs-feeaf763404fe7797b1daf2b0c009f35e8db1f9d.tar.gz
sshfs-feeaf763404fe7797b1daf2b0c009f35e8db1f9d.tar.bz2
sshfs-feeaf763404fe7797b1daf2b0c009f35e8db1f9d.zip
fix
-rw-r--r--ChangeLog11
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac9
-rw-r--r--sshfs.c41
4 files changed, 61 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index d500221..60122e8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2007-02-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * OpenSSH sftp-server can read requests faster, than it processes
+ them, when it's buffer is full it aborts. This can happen on a
+ large upload to a slow server. Work around this by limiting the
+ total size of outstanding reqests. Debian bug #365541. Tracked
+ down by Thue Janus Kristensen
+
+ * Add --disable-sshnodelay configure option. The sshnodelay.so
+ hack shouldnt be needed with OpenSSH client versions >= 4.3
+
2006-12-20 Miklos Szeredi <miklos@szeredi.hu>
* Work around write performace issues due to missing TCP_NODELAY
diff --git a/Makefile.am b/Makefile.am
index f55033d..4ea33ab 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -14,6 +14,7 @@ sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=26 -DLIBDIR=\"$(libdir)\"
EXTRA_DIST = sshnodelay.c FAQ.txt
CLEANFILES = sshnodelay.so
+if SSH_NODELAY_SO
all-local: sshnodelay.so
install-exec-local: sshnodelay.so
@@ -25,3 +26,4 @@ uninstall-local:
sshnodelay.so:
$(CC) -Wall -W -s --shared -fPIC $(sshnodelay_libs) sshnodelay.c -o sshnodelay.so
+endif
diff --git a/configure.ac b/configure.ac
index fb25d87..37601aa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -10,6 +10,15 @@ sshnodelay_libs=$LIBS
AC_SUBST(sshnodelay_libs)
LIBS=
+AC_ARG_ENABLE(sshnodelay,
+ [ --disable-sshnodelay Don't compile NODELAY workaround for ssh])
+
+if test "$enable_sshnodelay" != "no"; then
+ AC_DEFINE(SSH_NODELAY_WORKAROUND, 1, [Compile ssh NODELAY workaround])
+fi
+
+AM_CONDITIONAL(SSH_NODELAY_SO, test "$enable_sshnodelay" != "no")
+
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
PKG_CHECK_MODULES(SSHFS, [fuse >= 2.2 glib-2.0])
have_fuse_opt_parse=no
diff --git a/sshfs.c b/sshfs.c
index 499689d..202c6b2 100644
--- a/sshfs.c
+++ b/sshfs.c
@@ -124,6 +124,7 @@ struct request {
struct timeval start;
void *data;
request_func end_func;
+ size_t len;
struct list_head list;
};
@@ -160,6 +161,7 @@ struct sshfs {
int nodelay_workaround;
int nodelaysrv_workaround;
int truncate_workaround;
+ int buflimit_workaround;
int transform_symlinks;
int follow_symlinks;
int no_check_root;
@@ -186,6 +188,9 @@ struct sshfs {
unsigned blksize;
char *progname;
long modifver;
+ unsigned outstanding_len;
+ unsigned max_outstanding_len;
+ pthread_cond_t outstanding_cond;
};
static struct sshfs sshfs;
@@ -274,9 +279,12 @@ static struct fuse_opt workaround_opts[] = {
SSHFS_OPT("none", nodelay_workaround, 0),
SSHFS_OPT("none", nodelaysrv_workaround, 0),
SSHFS_OPT("none", truncate_workaround, 0),
+ SSHFS_OPT("none", buflimit_workaround, 0),
SSHFS_OPT("all", rename_workaround, 1),
SSHFS_OPT("all", nodelay_workaround, 1),
+ SSHFS_OPT("all", nodelaysrv_workaround, 1),
SSHFS_OPT("all", truncate_workaround, 1),
+ SSHFS_OPT("all", buflimit_workaround, 1),
SSHFS_OPT("rename", rename_workaround, 1),
SSHFS_OPT("norename", rename_workaround, 0),
SSHFS_OPT("nodelay", nodelay_workaround, 1),
@@ -285,6 +293,8 @@ static struct fuse_opt workaround_opts[] = {
SSHFS_OPT("nonodelaysrv", nodelaysrv_workaround, 0),
SSHFS_OPT("truncate", truncate_workaround, 1),
SSHFS_OPT("notruncate", truncate_workaround, 0),
+ SSHFS_OPT("buflimit", buflimit_workaround, 1),
+ SSHFS_OPT("nobuflimit", buflimit_workaround, 0),
FUSE_OPT_END
};
@@ -632,6 +642,7 @@ static void ssh_add_arg(const char *arg)
_exit(1);
}
+#ifdef SSH_NODELAY_WORKAROUND
static int do_ssh_nodelay_workaround(void)
{
char *oldpreload = getenv("LD_PRELOAD");
@@ -673,6 +684,7 @@ static int do_ssh_nodelay_workaround(void)
g_free(newpreload);
return 0;
}
+#endif
static int start_ssh(void)
{
@@ -692,8 +704,10 @@ static int start_ssh(void)
} else if (pid == 0) {
int devnull;
+#ifdef SSH_NODELAY_WORKAROUND
if (sshfs.nodelay_workaround && do_ssh_nodelay_workaround() == -1)
fprintf(stderr, "warning: ssh nodelay workaround disabled\n");
+#endif
if (sshfs.nodelaysrv_workaround) {
/* Hack to work around missing TCP_NODELAY setting in sshd */
@@ -833,7 +847,7 @@ static int sftp_send_iov(uint8_t type, uint32_t id, struct iovec iov[],
unsigned nout = 0;
assert(count <= SFTP_MAX_IOV - 1);
- buf_init(&buf, 5);
+ buf_init(&buf, 9);
buf_add_uint32(&buf, iov_length(iov, count) + 5);
buf_add_uint8(&buf, type);
buf_add_uint32(&buf, id);
@@ -958,8 +972,13 @@ static void *process_requests(void *data_)
GUINT_TO_POINTER(id));
if (req == NULL)
fprintf(stderr, "request %i not found\n", id);
- else
+ else {
+ int was_over = sshfs.outstanding_len > sshfs.max_outstanding_len;
+ sshfs.outstanding_len -= req->len;
+ if (was_over && sshfs.outstanding_len <= sshfs.max_outstanding_len)
+ pthread_cond_broadcast(&sshfs.outstanding_cond);
g_hash_table_remove(sshfs.reqtab, GUINT_TO_POINTER(id));
+ }
pthread_mutex_unlock(&sshfs.lock);
if (req != NULL) {
struct timeval now;
@@ -1353,6 +1372,11 @@ static int sftp_request_send(uint8_t type, struct iovec *iov, size_t count,
pthread_mutex_unlock(&sshfs.lock);
goto out;
}
+ req->len = iov_length(iov, count) + 9;
+ sshfs.outstanding_len += req->len;
+ while (sshfs.outstanding_len > sshfs.max_outstanding_len)
+ pthread_cond_wait(&sshfs.outstanding_cond, &sshfs.lock);
+
g_hash_table_insert(sshfs.reqtab, GUINT_TO_POINTER(id), req);
gettimeofday(&req->start, NULL);
DEBUG("[%05i] %s\n", id, type_name(type));
@@ -2356,6 +2380,7 @@ static int processing_init(void)
{
pthread_mutex_init(&sshfs.lock, NULL);
pthread_mutex_init(&sshfs.lock_write, NULL);
+ pthread_cond_init(&sshfs.outstanding_cond, NULL);
sshfs.reqtab = g_hash_table_new(NULL, NULL);
if (!sshfs.reqtab) {
fprintf(stderr, "failed to create hash table\n");
@@ -2422,9 +2447,12 @@ static void usage(const char *progname)
" none no workarounds enabled\n"
" all all workarounds enabled\n"
" [no]rename fix renaming to existing file (default: off)\n"
+#ifdef SSH_NODELAY_WORKAROUND
" [no]nodelay set nodelay tcp flag in ssh (default: on)\n"
+#endif
" [no]nodelaysrv set nodelay tcp flag in sshd (default: on)\n"
" [no]truncate fix truncate for old servers (default: off)\n"
+" [no]buflimit fix buffer fillup bug in server (default: on)\n"
" -o idmap=TYPE user/group ID mapping, possible types are:\n"
" none no translation of the ID space (default)\n"
" user only translate UID of connecting user\n"
@@ -2584,6 +2612,7 @@ int main(int argc, char *argv[])
sshfs.nodelaysrv_workaround = 1;
sshfs.rename_workaround = 0;
sshfs.truncate_workaround = 0;
+ sshfs.buflimit_workaround = 1;
sshfs.ssh_ver = 2;
sshfs.progname = argv[0];
ssh_add_arg("ssh");
@@ -2595,6 +2624,14 @@ int main(int argc, char *argv[])
parse_workarounds() == -1)
exit(1);
+ if (sshfs.buflimit_workaround)
+ /* Work around buggy sftp-server in OpenSSH. Without this on
+ a slow server a 10Mbyte buffer would fill up and the server
+ would abort */
+ sshfs.max_outstanding_len = 8388608;
+ else
+ sshfs.max_outstanding_len = ~0;
+
if (!sshfs.host) {
fprintf(stderr, "missing host\n");
fprintf(stderr, "see `%s -h' for usage\n", argv[0]);