chiark / gitweb /
readahead: exit after a maximum runtime
[elogind.git] / src / readahead-collect.c
index 93a04521f3fb82a7dd9d3385f36ff18fe14f1525..6705615d11e7267028c48476f68e5269537c8643 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/fs.h>
 #include <linux/fiemap.h>
 #include <sys/ioctl.h>
+#include <sys/vfs.h>
 
 #include "missing.h"
 #include "util.h"
 #include "ioprio.h"
 #include "readahead-common.h"
 
-/*
-   fixme:
+#define MINCORE_VEC_SIZE (READAHEAD_FILE_SIZE_MAX/PAGE_SIZE)
+#define TIMEOUT_USEC (2*USEC_PER_MINUTE)
+
+/* fixme:
+ *
+ * - detect ssd on btrfs/lvm...
+ * - read ahead directories
+ * - sd_readahead_cancel
+ * - gzip?
+ * - oom adjust
+ * - remount rw
+ * - are filenames from anotify normalized regards /../ and // and /./?
+ * - does ioprio_set work with fadvise()?
+ */
+
+static int btrfs_defrag(int fd) {
+        struct btrfs_ioctl_vol_args data;
 
-     - BTRFS_IOC_DEFRAG
-*/
+        zero(data);
+        data.fd = fd;
 
-#define MINCORE_VEC_SIZE (READAHEAD_FILE_SIZE_MAX/PAGE_SIZE)
+        return ioctl(fd, BTRFS_IOC_DEFRAG, &data);
+}
 
-static int pack_file(FILE *pack, const char *fn) {
+static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
         struct stat st;
         void *start = MAP_FAILED;
         uint8_t vec[MINCORE_VEC_SIZE];
@@ -78,6 +95,9 @@ static int pack_file(FILE *pack, const char *fn) {
                 goto finish;
         }
 
+        if (on_btrfs)
+                btrfs_defrag(fd);
+
         l = PAGE_ALIGN(st.st_size);
         if ((start = mmap(NULL, l, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
                 log_warning("mmap(%s) failed: %m", fn);
@@ -97,7 +117,7 @@ static int pack_file(FILE *pack, const char *fn) {
         pages = l / PAGE_SIZE;
         mapped = false;
         for (c = 0; c < pages; c++) {
-                bool new_mapped = (vec[c] & 1);
+                bool new_mapped = !!(vec[c] & 1);
 
                 if (!mapped && new_mapped)
                         b = c;
@@ -190,7 +210,9 @@ static int collect(const char *root) {
         sigset_t mask;
         FILE *pack = NULL;
         char *pack_fn_new = NULL, *pack_fn = NULL;
-        bool on_ssd;
+        bool on_ssd, on_btrfs;
+        struct statfs sfs;
+        usec_t not_after;
 
         assert(root);
 
@@ -213,7 +235,7 @@ static int collect(const char *root) {
                 goto finish;
         }
 
-        if ((fanotify_fd = fanotify_init(FAN_CLOEXEC, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME)) < 0)  {
+        if ((fanotify_fd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME)) < 0)  {
                 log_error("Failed to create fanotify object: %m");
                 r = -errno;
                 goto finish;
@@ -225,6 +247,8 @@ static int collect(const char *root) {
                 goto finish;
         }
 
+        not_after = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
+
         my_pid = getpid();
 
         zero(pollfd);
@@ -246,8 +270,21 @@ static int collect(const char *root) {
                 } data;
                 ssize_t n;
                 struct fanotify_event_metadata *m;
+                usec_t t;
+                int h;
+
+                if (hashmap_size(files) > READAHEAD_FILES_MAX) {
+                        log_debug("Reached maximum number of read ahead files, ending collection.");
+                        break;
+                }
+
+                t = now(CLOCK_MONOTONIC);
+                if (t >= not_after) {
+                        log_debug("Reached maximum collection time, ending collection.");
+                        break;
+                }
 
-                if (poll(pollfd, _FD_MAX, -1) < 0) {
+                if ((h = poll(pollfd, _FD_MAX, (int) ((not_after - t) / USEC_PER_MSEC))) < 0) {
 
                         if (errno == EINTR)
                                 continue;
@@ -260,6 +297,11 @@ static int collect(const char *root) {
                 if (pollfd[FD_SIGNAL].revents != 0)
                         break;
 
+                if (h == 0) {
+                        log_debug("Reached maximum collection time, ending collection.");
+                        break;
+                }
+
                 if ((n = read(fanotify_fd, &data, sizeof(data))) < 0) {
 
                         if (errno == EINTR || errno == EAGAIN)
@@ -270,8 +312,7 @@ static int collect(const char *root) {
                         goto finish;
                 }
 
-                m = &data.metadata;
-                while (FAN_EVENT_OK(m, n)) {
+                for (m = &data.metadata; FAN_EVENT_OK(m, n); m = FAN_EVENT_NEXT(m, n)) {
 
                         if (m->pid != my_pid && m->fd >= 0) {
                                 char fn[PATH_MAX];
@@ -291,10 +332,7 @@ static int collect(const char *root) {
                                                 ul = fd_first_block(m->fd);
 
                                                 if ((k = hashmap_put(files, p, ULONG_TO_PTR(ul))) < 0) {
-
-                                                        if (k != -EEXIST)
-                                                                log_warning("set_put() failed: %s", strerror(-k));
-
+                                                        log_warning("set_put() failed: %s", strerror(-k));
                                                         free(p);
                                                 }
                                         }
@@ -305,10 +343,7 @@ static int collect(const char *root) {
 
                         if (m->fd)
                                 close_nointr_nofail(m->fd);
-
-                        m = FAN_EVENT_NEXT(m, n);
                 }
-
         }
 
         if (fanotify_fd >= 0) {
@@ -321,6 +356,9 @@ static int collect(const char *root) {
         on_ssd = fs_on_ssd(root);
         log_debug("On SSD: %s", yes_no(on_ssd));
 
+        on_btrfs = statfs(root, &sfs) >= 0 && sfs.f_type == BTRFS_SUPER_MAGIC;
+        log_debug("On btrfs: %s", yes_no(on_btrfs));
+
         asprintf(&pack_fn, "%s/.readahead", root);
         asprintf(&pack_fn_new, "%s/.readahead.new", root);
 
@@ -339,13 +377,13 @@ static int collect(const char *root) {
         fputs(CANONICAL_HOST "\n", pack);
         putc(on_ssd ? 'S' : 'R', pack);
 
-        if (on_ssd) {
+        if (on_ssd || on_btrfs) {
 
-                /* On SSD, just write things out in the order the
-                 * files where accessed */
+                /* On SSD or on btrfs, just write things out in the
+                 * order the files were accessed. */
 
                 HASHMAP_FOREACH_KEY(q, p, files, i)
-                        pack_file(pack, p);
+                        pack_file(pack, p, on_btrfs);
         } else {
                 struct item *ordered, *j;
                 unsigned k, n;
@@ -374,7 +412,7 @@ static int collect(const char *root) {
                 qsort(ordered, n, sizeof(struct item), qsort_compare);
 
                 for (k = 0; k < n; k++)
-                        pack_file(pack, ordered[k].path);
+                        pack_file(pack, ordered[k].path, on_btrfs);
 
                 free(ordered);
         }
@@ -424,13 +462,17 @@ finish:
 }
 
 int main(int argc, char *argv[]) {
-        /* log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); */
+
+        log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
         log_parse_environment();
         log_open();
 
-        log_set_max_level(LOG_DEBUG);
+        if (!enough_ram()) {
+                log_info("Disabling readahead collector due to low memory.");
+                return 0;
+        }
 
-        if (collect("/") < 0)
+        if (collect(argc >= 2 ? argv[1] : "/") < 0)
                 return 1;
 
         return 0;