chiark / gitweb /
readahead: store inode numbers in pack file
[elogind.git] / src / readahead / readahead-collect.c
index 6bff32f4dd0a1e8084f2b3af4c76faa6717fac18..359b97bf040cccdfcd3d4066bcfb68ae20edf823 100644 (file)
@@ -6,16 +6,16 @@
   Copyright 2010 Lennart Poettering
 
   systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
   (at your option) any later version.
 
   systemd is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
+  Lesser General Public License for more details.
 
-  You should have received a copy of the GNU General Public License
+  You should have received a copy of the GNU Lesser General Public License
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
@@ -86,6 +86,7 @@ static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
         void *start = MAP_FAILED;
         uint8_t *vec;
         uint32_t b, c;
+        uint64_t inode;
         size_t l, pages;
         bool mapped;
         int r = 0, fd = -1, k;
@@ -93,7 +94,8 @@ static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
         assert(pack);
         assert(fn);
 
-        if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) {
+        fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW);
+        if (fd < 0) {
 
                 if (errno == ENOENT)
                         return 0;
@@ -106,7 +108,8 @@ static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
                 goto finish;
         }
 
-        if ((k = file_verify(fd, fn, arg_file_size_max, &st)) <= 0) {
+        k = file_verify(fd, fn, arg_file_size_max, &st);
+        if (k <= 0) {
                 r = k;
                 goto finish;
         }
@@ -115,14 +118,14 @@ static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
                 btrfs_defrag(fd);
 
         l = PAGE_ALIGN(st.st_size);
-        if ((start = mmap(NULL, l, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+        start = mmap(NULL, l, PROT_READ, MAP_SHARED, fd, 0);
+        if (start == MAP_FAILED) {
                 log_warning("mmap(%s) failed: %m", fn);
                 r = -errno;
                 goto finish;
         }
 
         pages = l / page_size();
-
         vec = alloca(pages);
         memset(vec, 0, pages);
         if (mincore(start, l, vec) < 0) {
@@ -134,6 +137,10 @@ static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
         fputs(fn, pack);
         fputc('\n', pack);
 
+        /* Store the inode, so that we notice when the file is deleted */
+        inode = (uint64_t) st.st_ino;
+        fwrite(&inode, sizeof(inode), 1, pack);
+
         mapped = false;
         for (c = 0; c < pages; c++) {
                 bool new_mapped = !!(vec[c] & 1);
@@ -233,10 +240,25 @@ static int collect(const char *root) {
         bool on_ssd, on_btrfs;
         struct statfs sfs;
         usec_t not_after;
+        uint64_t previous_block_readahead;
+        bool previous_block_readahead_set = false;
 
         assert(root);
 
-        write_one_line_file("/proc/self/oom_score_adj", "1000");
+        if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
+                log_error("Out of memory");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        /* If there's no pack file yet we lower the kernel readahead
+         * so that mincore() is accurate. If there is a pack file
+         * already we assume it is accurate enough so that kernel
+         * readahead is never triggered. */
+        previous_block_readahead_set =
+                access(pack_fn, F_OK) < 0 &&
+                block_get_readahead(root, &previous_block_readahead) >= 0 &&
+                block_set_readahead(root, 8*1024) >= 0;
 
         if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)) < 0)
                 log_warning("Failed to set IDLE IO priority class: %m");
@@ -458,22 +480,20 @@ done:
         on_btrfs = statfs(root, &sfs) >= 0 && (long) sfs.f_type == (long) 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);
-
-        if (!pack_fn || !pack_fn_new) {
+        if (asprintf(&pack_fn_new, "%s/.readahead.new", root) < 0) {
                 log_error("Out of memory");
                 r = -ENOMEM;
                 goto finish;
         }
 
-        if (!(pack = fopen(pack_fn_new, "we"))) {
+        pack = fopen(pack_fn_new, "we");
+        if (!pack) {
                 log_error("Failed to open pack file: %m");
                 r = -errno;
                 goto finish;
         }
 
-        fputs(CANONICAL_HOST "\n", pack);
+        fputs(CANONICAL_HOST ";VERSION=2\n", pack);
         putc(on_ssd ? 'S' : 'R', pack);
 
         if (on_ssd || on_btrfs) {
@@ -551,7 +571,6 @@ finish:
                 fclose(pack);
                 unlink(pack_fn_new);
         }
-
         free(pack_fn_new);
         free(pack_fn);
 
@@ -560,6 +579,16 @@ finish:
 
         hashmap_free(files);
 
+        if (previous_block_readahead_set) {
+                uint64_t bytes;
+
+                /* Restore the original kernel readahead setting if we
+                 * changed it, and nobody has overwritten it since
+                 * yet. */
+                if (block_get_readahead(root, &bytes) >= 0 && bytes == 8*1024)
+                        block_set_readahead(root, previous_block_readahead);
+        }
+
         return r;
 }
 
@@ -654,17 +683,23 @@ int main(int argc, char *argv[]) {
         int r;
         const char *root;
 
-        log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
+        log_set_target(LOG_TARGET_AUTO);
         log_parse_environment();
         log_open();
 
         umask(0022);
 
-        if ((r = parse_argv(argc, argv)) <= 0)
+        r = parse_argv(argc, argv);
+        if (r <= 0)
                 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 
         root = optind < argc ? argv[optind] : "/";
 
+        /* Skip this step on read-only media. Note that we check the
+         * underlying block device here, not he read-only flag of the
+         * file system on top, since that one is most likely mounted
+         * read-only anyway at boot, even if the underlying block
+         * device is theoretically writable. */
         if (fs_on_read_only(root) > 0) {
                 log_info("Disabling readahead collector due to read-only media.");
                 return 0;
@@ -675,12 +710,8 @@ int main(int argc, char *argv[]) {
                 return 0;
         }
 
-        if (detect_virtualization(NULL) > 0) {
-                log_info("Disabling readahead collector due to execution in virtualized environment.");
-                return 0;
-        }
-
-        if (!(shared = shared_get()))
+        shared = shared_get();
+        if (!shared)
                 return 1;
 
         shared->collect = getpid();