[PATCH] Use sendfile to copy data between file descriptors
Bartosz Golaszewski
bartekgola at gmail.com
Mon Oct 20 11:36:45 UTC 2014
Busybox already uses sendfile in httpd. This patch proposes to use it
globally to copy data between file descriptors.
It speeds up the copying on slow systems a lot - below are the times needed
to copy a 450Mb file with and without this option enabled on a BeagleBone
Black:
sendfile:
user 0m0.000s
sys 0m8.170s
read/write:
user 0m0.470s
sys 0m16.300s
It doesn't add a lot of bloat either:
function old new delta
bb_full_fd_action 233 311 +78
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/0 up/down: 78/0) Total: 78 bytes
text data bss dec hex filename
812839 4123 9552 826514 c9c92 busybox_old
812917 4123 9552 826592 c9ce0 busybox_unstripped
This patch also moves USE_SENDFILE feature from httpd subconfiguration
to global settings.
Signed-off-by: Bartosz Golaszewski <bartekgola at gmail.com>
---
Config.in | 8 +++++++
libbb/copyfd.c | 59 ++++++++++++++++++++++++++++++++++++++++-----------
networking/Config.src | 8 -------
networking/httpd.c | 6 +++---
4 files changed, 58 insertions(+), 23 deletions(-)
diff --git a/Config.in b/Config.in
index b83beb5..4b5ea69 100644
--- a/Config.in
+++ b/Config.in
@@ -264,6 +264,14 @@ config PAM
Use PAM in some busybox applets (currently login and httpd) instead
of direct access to password database.
+config FEATURE_USE_SENDFILE
+ bool "Use sendfile system call"
+ default y
+ depends on HTTPD
+ help
+ When enabled, busybox will use the kernel sendfile() function
+ instead of read/write loops where applicable.
+
config LONG_OPTS
bool "Support for --long-options"
default y
diff --git a/libbb/copyfd.c b/libbb/copyfd.c
index eda2747..e10fe82 100644
--- a/libbb/copyfd.c
+++ b/libbb/copyfd.c
@@ -9,6 +9,28 @@
#include "libbb.h"
+#if ENABLE_FEATURE_USE_SENDFILE
+#include <sys/sendfile.h>
+#endif
+
+/*
+ * Returns 1 if all bytes have been copied, 0 otherwise.
+ */
+static int check_status(int *status, off_t *size, off_t *total, ssize_t rd)
+{
+ *total += rd;
+ if (*status < 0) { /* if we aren't copying till EOF... */
+ *size -= rd;
+ if (!(*size)) {
+ /* 'size' bytes copied - all done */
+ *status = 0;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/* Used by NOFORK applets (e.g. cat) - must not use xmalloc.
* size < 0 means "ignore write errors", used by tar --to-command
* size = 0 means "copy till EOF"
@@ -18,6 +40,7 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
int status = -1;
off_t total = 0;
bool continue_on_write_error = 0;
+ ssize_t rd;
#if CONFIG_FEATURE_COPYBUF_KB <= 4
char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024];
enum { buffer_size = sizeof(buffer) };
@@ -56,10 +79,29 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
status = 1; /* copy until eof */
}
- while (1) {
- ssize_t rd;
+#if ENABLE_FEATURE_USE_SENDFILE
+ {
+ ssize_t sz = size && (size < buffer_size)
+ ? size : MAXINT(ssize_t) - 0xffff;
- rd = safe_read(src_fd, buffer, size > buffer_size ? buffer_size : size);
+ while (1) {
+ rd = sendfile(dst_fd, src_fd, NULL, sz);
+ if (rd <= 0) {
+ /* Might be EOF, might be an error,
+ * to make sure fall back to the read-write
+ * loop.
+ */
+ break;
+ } else if (check_status(&status, &size, &total, rd)) {
+ break;
+ }
+ }
+ }
+#endif
+
+ while (1) {
+ rd = safe_read(src_fd, buffer,
+ size > buffer_size ? buffer_size : size);
if (!rd) { /* eof - all done */
status = 0;
@@ -80,15 +122,8 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
dst_fd = -1;
}
}
- total += rd;
- if (status < 0) { /* if we aren't copying till EOF... */
- size -= rd;
- if (!size) {
- /* 'size' bytes copied - all done */
- status = 0;
- break;
- }
- }
+ if (check_status(&status, &size, &total, rd))
+ break;
}
out:
diff --git a/networking/Config.src b/networking/Config.src
index e566469..15a6968 100644
--- a/networking/Config.src
+++ b/networking/Config.src
@@ -181,14 +181,6 @@ config FEATURE_HTTPD_RANGES
"Range: bytes=NNN-[MMM]" header. Allows for resuming interrupted
downloads, seeking in multimedia players etc.
-config FEATURE_HTTPD_USE_SENDFILE
- bool "Use sendfile system call"
- default y
- depends on HTTPD
- help
- When enabled, httpd will use the kernel sendfile() function
- instead of read/write loop.
-
config FEATURE_HTTPD_SETUID
bool "Enable -u <user> option"
default y
diff --git a/networking/httpd.c b/networking/httpd.c
index 621d9cd..9cf0804 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -133,7 +133,7 @@
# include <security/pam_appl.h>
# include <security/pam_misc.h>
#endif
-#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
+#if ENABLE_FEATURE_USE_SENDFILE
# include <sys/sendfile.h>
#endif
/* amount of buffering in a pipe */
@@ -1624,7 +1624,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
#endif
if (what & SEND_HEADERS)
send_headers(HTTP_OK);
-#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
+#if ENABLE_FEATURE_USE_SENDFILE
{
off_t offset = range_start;
while (1) {
@@ -1654,7 +1654,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
break;
}
if (count < 0) {
- IF_FEATURE_HTTPD_USE_SENDFILE(fin:)
+ IF_FEATURE_USE_SENDFILE(fin:)
if (verbose > 1)
bb_perror_msg("error");
}
--
2.1.1
More information about the busybox
mailing list