crash-pipe: Introduce option to copy core using splice(2) 47/125747/1
authorKarol Lewandowski <k.lewandowsk@samsung.com>
Wed, 15 Mar 2017 15:39:51 +0000 (16:39 +0100)
committerKarol Lewandowski <k.lewandowsk@samsung.com>
Tue, 18 Apr 2017 13:02:50 +0000 (15:02 +0200)
Linux splice(2) avoids user-space copy and is effectively faster than
regular read(2) + write(2).

According to (ftrace) test below splice is about 20% faster.

  sysctl -w kernel.core_pattern='|/usr/libexec/core-pipe --save-core /opt/usr/share/temp/core.%p'
  cd /sys/fs/kernel/tracing/
  echo do_coredump > set_ftrace_filter
  echo function_graph > current_tracer
  cat trace_pipe

/* splice */

 2) $ 2183078 us |  } /* do_coredump */
 4) $ 2127946 us |  } /* do_coredump */
 1) $ 2377004 us |  } /* do_coredump */
 1) $ 2088114 us |  } /* do_coredump */

/* read/write */
 1) $ 3088297 us |  } /* do_coredump */
 1) $ 2953330 us |  } /* do_coredump */
 1) $ 2647784 us |  } /* do_coredump */
 3) $ 2271315 us |  }

Change-Id: Id2c681a364ce96d98c70329abdf4a64f6d0b405d

src/crash-pipe/crash-pipe.c

index 290513c..0dbea20 100644 (file)
@@ -426,12 +426,50 @@ static void report(int argc, char *argv[])
        }
 }
 
-static int save_core(const char *core_path)
+static int copy_fd_simple(int in, int out)
 {
-       int fd;
        static char buf[4096];
        int readb, remaining;
-       int ret = 0;
+
+       while ((readb = read(in, buf, sizeof(buf))) > 0) {
+               int n;
+
+               for (n = 0, remaining = readb ; remaining > 0; remaining -= n) {
+                       n = write(out, buf, remaining);
+                       if (n == -1)
+                               return -errno;
+               }
+       }
+
+       return 1;
+}
+
+static int copy_fd_splice(int in, int out)
+{
+       ssize_t s;
+       _Bool copied_data = 0;
+
+       const ssize_t max_splice = SSIZE_MAX;
+
+       do {
+               s = splice(in, NULL, out, NULL, max_splice, SPLICE_F_MOVE);
+               // We can try to fallback to simple copy only if first splice failed
+               // (ie. we have not consumed any data yet)
+               if (!copied_data && s > 0)
+                       copied_data = 1;
+
+       } while (s > 0);
+
+       if (s < 0)
+               return copied_data ? -errno : 0;
+       else
+               return 1;
+}
+
+static int save_core(const char *core_path)
+{
+       int fd;
+       int r = 0;
 
        fd = open(core_path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
        if (fd == -1) {
@@ -439,24 +477,18 @@ static int save_core(const char *core_path)
                return -errno;
        }
 
-       while ((readb = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
-               int n;
-
-               for (n = 0, remaining = readb ; remaining > 0; remaining -= n) {
-                       n = write(fd, buf, remaining);
-                       if (n == -1) {
-                               ret = -errno;
-                               syslog(LOG_ERR, "crash-pipe: Error while saving core file %s: %m. Removing core.\n", core_path);
-                               (void)unlink(core_path); // XXX check errors here too
-                               goto out;
-                       }
-               }
+       r = copy_fd_splice(STDIN_FILENO, fd);
+       if (r == 0) {
+               lseek(fd, SEEK_SET, 0);
+               r = copy_fd_simple(STDIN_FILENO, fd);
+       }
+       if (r < 0) {
+               syslog(LOG_ERR, "crash-pipe: Error while saving core file %s: %m. Removing core.\n", core_path);
+               (void)unlink(core_path); // XXX check errors here too
        }
 
-out:
        close(fd);
-
-       return ret;
+       return r;
 }