chiark / gitweb /
readhead: temporarily lower the kernel's read_ahead_kb setting while collecting
authorLennart Poettering <lennart@poettering.net>
Thu, 3 May 2012 22:13:20 +0000 (00:13 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 3 May 2012 22:13:20 +0000 (00:13 +0200)
While collecting readahead data we want to know exactly what userspace
accesses unblurred by the kernel's read_ahead_kb. Hence lower this
during collection, and raise it afterwards.

This is mostly based on ideas and code by Auke Kok.

src/readahead/readahead-collect.c
src/readahead/readahead-common.c
src/readahead/readahead-common.h
src/readahead/readahead-replay.c

index 008ede3964996a5e85c33cdd02370d8f0c625052..70e0f66eda2b51d5a523796536fcadd34ebfc19e 100644 (file)
@@ -233,9 +233,26 @@ 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);
 
+        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;
+
         write_one_line_file("/proc/self/oom_score_adj", "1000");
 
         if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)) < 0)
@@ -458,10 +475,7 @@ 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;
@@ -551,7 +565,6 @@ finish:
                 fclose(pack);
                 unlink(pack_fn_new);
         }
-
         free(pack_fn_new);
         free(pack_fn);
 
@@ -560,6 +573,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;
 }
 
index 4e8e63697541f51107ab5be2c1b93337935fe36b..99dbac2525a8aa739830d613b1ea97e626d6a762 100644 (file)
@@ -210,7 +210,7 @@ finish:
 
 #define BUMP_REQUEST_NR (16*1024)
 
-int bump_request_nr(const char *p) {
+int block_bump_request_nr(const char *p) {
         struct stat st;
         uint64_t u;
         char *ap = NULL, *line = NULL;
@@ -267,3 +267,83 @@ finish:
 
         return r;
 }
+
+int block_get_readahead(const char *p, uint64_t *bytes) {
+        struct stat st;
+        char *ap = NULL, *line = NULL;
+        int r;
+        dev_t d;
+        uint64_t u;
+
+        assert(p);
+        assert(bytes);
+
+        if (stat(p, &st) < 0)
+                return -errno;
+
+        if (major(st.st_dev) == 0)
+                return 0;
+
+        d = st.st_dev;
+        block_get_whole_disk(d, &d);
+
+        if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        r = read_one_line_file(ap, &line);
+        if (r < 0)
+                goto finish;
+
+        r = safe_atou64(line, &u);
+        if (r < 0)
+                goto finish;
+
+        *bytes = u * 1024ULL;
+
+finish:
+        free(ap);
+        free(line);
+
+        return r;
+}
+
+int block_set_readahead(const char *p, uint64_t bytes) {
+        struct stat st;
+        char *ap = NULL, *line = NULL;
+        int r;
+        dev_t d;
+
+        assert(p);
+        assert(bytes);
+
+        if (stat(p, &st) < 0)
+                return -errno;
+
+        if (major(st.st_dev) == 0)
+                return 0;
+
+        d = st.st_dev;
+        block_get_whole_disk(d, &d);
+
+        if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (asprintf(&line, "%llu", (unsigned long long) bytes / 1024ULL) < 0) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        r = write_one_line_file(ap, line);
+        if (r < 0)
+                goto finish;
+
+finish:
+        free(ap);
+        free(line);
+
+        return r;
+}
index b4eab71b1a9f6ce703f490d26452a7bbf143c5e3..9962dd527a242a37e2226fa56628ae7553eddade 100644 (file)
@@ -45,6 +45,9 @@ typedef struct ReadaheadShared {
 
 ReadaheadShared *shared_get(void);
 
-int bump_request_nr(const char *p);
+int block_bump_request_nr(const char *p);
+
+int block_get_readahead(const char *p, uint64_t *bytes);
+int block_set_readahead(const char *p, uint64_t bytes);
 
 #endif
index f91020e805fd44cc92ad2c446ea96d4965239183..0b7e6df946aab24b7311b3de133bf601049cc730 100644 (file)
@@ -133,7 +133,7 @@ static int replay(const char *root) {
         assert(root);
 
         write_one_line_file("/proc/self/oom_score_adj", "1000");
-        bump_request_nr(root);
+        block_bump_request_nr(root);
 
         if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
                 log_error("Out of memory");