init: add option to read file fully on readahead

Bug: 62413151
Test: boottime, dumpcache
Change-Id: I1a7b69f0619428e4db31c5a7639c5d895c89ecdb
diff --git a/init/README.md b/init/README.md
index b681f21..7df37ff 100644
--- a/init/README.md
+++ b/init/README.md
@@ -453,8 +453,9 @@
 `rmdir <path>`
 > Calls rmdir(2) on the given path.
 
-`readahead <file|dir>`
+`readahead <file|dir> [--fully]`
 > Calls readahead(2) on the file or files within given directory.
+  Use option --fully to read the full file content.
 
 `setprop <name> <value>`
 > Set system property _name_ to _value_. Properties are expanded
diff --git a/init/builtins.cpp b/init/builtins.cpp
index e2e3d93..140ef75 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -56,6 +56,7 @@
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
+#include <system/thread_defs.h>
 
 #include "action.h"
 #include "bootchart.h"
@@ -670,6 +671,29 @@
     return Success();
 }
 
+static Result<Success> readahead_file(const std::string& filename, bool fully) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY)));
+    if (fd == -1) {
+        return ErrnoError() << "Error opening file";
+    }
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED)) {
+        return ErrnoError() << "Error posix_fadvise file";
+    }
+    if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+        return ErrnoError() << "Error readahead file";
+    }
+    if (fully) {
+        char buf[BUFSIZ];
+        ssize_t n;
+        while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+        }
+        if (n != 0) {
+            return ErrnoError() << "Error reading file";
+        }
+    }
+    return Success();
+}
+
 static Result<Success> do_readahead(const std::vector<std::string>& args) {
     struct stat sb;
 
@@ -677,6 +701,10 @@
         return ErrnoError() << "Error opening " << args[1];
     }
 
+    bool readfully = false;
+    if (args.size() == 3 && args[2] == "--fully") {
+        readfully = true;
+    }
     // We will do readahead in a forked process in order not to block init
     // since it may block while it reads the
     // filesystem metadata needed to locate the requested blocks.  This
@@ -685,15 +713,16 @@
     // the requested data has been read.
     pid_t pid = fork();
     if (pid == 0) {
+        if (setpriority(PRIO_PROCESS, 0, static_cast<int>(ANDROID_PRIORITY_LOWEST)) != 0) {
+            PLOG(WARNING) << "setpriority failed";
+        }
+        if (android_set_ioprio(0, IoSchedClass_IDLE, 7)) {
+            PLOG(WARNING) << "ioprio_get failed";
+        }
         android::base::Timer t;
         if (S_ISREG(sb.st_mode)) {
-            android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(args[1].c_str(), O_RDONLY)));
-            if (fd == -1) {
-                PLOG(ERROR) << "Error opening file: " << args[1];
-                _exit(EXIT_FAILURE);
-            }
-            if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
-                PLOG(ERROR) << "Error readahead file: " << args[1];
+            if (auto result = readahead_file(args[1], readfully); !result) {
+                LOG(WARNING) << "Unable to readahead '" << args[1] << "': " << result.error();
                 _exit(EXIT_FAILURE);
             }
         } else if (S_ISDIR(sb.st_mode)) {
@@ -708,19 +737,15 @@
             for (FTSENT* ftsent = fts_read(fts.get()); ftsent != nullptr;
                  ftsent = fts_read(fts.get())) {
                 if (ftsent->fts_info & FTS_F) {
-                    android::base::unique_fd fd(
-                        TEMP_FAILURE_RETRY(open(ftsent->fts_accpath, O_RDONLY)));
-                    if (fd == -1) {
-                        PLOG(ERROR) << "Error opening file: " << args[1];
-                        continue;
-                    }
-                    if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
-                        PLOG(ERROR) << "Unable to readahead on file: " << ftsent->fts_accpath;
+                    const std::string filename = ftsent->fts_accpath;
+                    if (auto result = readahead_file(filename, readfully); !result) {
+                        LOG(WARNING)
+                            << "Unable to readahead '" << filename << "': " << result.error();
                     }
                 }
             }
         }
-        LOG(INFO) << "Readahead " << args[1] << " took " << t;
+        LOG(INFO) << "Readahead " << args[1] << " took " << t << " asynchronously";
         _exit(0);
     } else if (pid < 0) {
         return ErrnoError() << "Fork failed";
@@ -949,7 +974,7 @@
         {"mount_all",               {1,     kMax, do_mount_all}},
         {"mount",                   {3,     kMax, do_mount}},
         {"umount",                  {1,     1,    do_umount}},
-        {"readahead",               {1,     1,    do_readahead}},
+        {"readahead",               {1,     2,    do_readahead}},
         {"restart",                 {1,     1,    do_restart}},
         {"restorecon",              {1,     kMax, do_restorecon}},
         {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},