defer firmware load until after filesystems are mounted

In some situations a driver could try to request firmware before
/system is mounted.  Previously we'd fail the request.  Now we
will retry the read-from-filesystem every 100ms until we find the
firmware or we've finished the "fs" and "post-fs" stages of init.

Change-Id: Ie32402f7d41c818bf20f3297286ed5f99705b72c
diff --git a/init/devices.c b/init/devices.c
index 036b8f7..08d23e3 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -551,13 +551,19 @@
     return ret;
 }
 
+static int is_booting(void)
+{
+    return access("/dev/.booting", F_OK) == 0;
+}
+
 static void process_firmware_event(struct uevent *uevent)
 {
     char *root, *loading, *data, *file1 = NULL, *file2 = NULL;
     int l, loading_fd, data_fd, fw_fd;
+    int booting = is_booting();
 
-    log_event_print("firmware event { '%s', '%s' }\n",
-                    uevent->path, uevent->firmware);
+    INFO("firmware: loading '%s' for '%s'\n",
+         uevent->firmware, uevent->path);
 
     l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
     if (l == -1)
@@ -587,19 +593,29 @@
     if(data_fd < 0)
         goto loading_close_out;
 
+try_loading_again:
     fw_fd = open(file1, O_RDONLY);
     if(fw_fd < 0) {
         fw_fd = open(file2, O_RDONLY);
         if (fw_fd < 0) {
+            if (booting) {
+                    /* If we're not fully booted, we may be missing
+                     * filesystems needed for firmware, wait and retry.
+                     */
+                usleep(100000);
+                booting = is_booting();
+                goto try_loading_again;
+            }
+            INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno);
             write(loading_fd, "-1", 2);
             goto data_close_out;
         }
     }
 
     if(!load_firmware(fw_fd, loading_fd, data_fd))
-        log_event_print("firmware copy success { '%s', '%s' }\n", root, uevent->firmware);
+        INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
     else
-        log_event_print("firmware copy failure { '%s', '%s' }\n", root, uevent->firmware);
+        INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
 
     close(fw_fd);
 data_close_out:
@@ -620,7 +636,6 @@
 static void handle_firmware_event(struct uevent *uevent)
 {
     pid_t pid;
-    int status;
     int ret;
 
     if(strcmp(uevent->subsystem, "firmware"))
@@ -634,10 +649,6 @@
     if (!pid) {
         process_firmware_event(uevent);
         exit(EXIT_SUCCESS);
-    } else {
-        do {
-            ret = waitpid(pid, &status, 0);
-        } while (ret == -1 && errno == EINTR);
     }
 }
 
diff --git a/init/init.c b/init/init.c
index e13d4b1..1e31cf9 100755
--- a/init/init.c
+++ b/init/init.c
@@ -651,6 +651,10 @@
         ERROR("init startup failure\n");
         exit(1);
     }
+
+        /* signal that we hit this point */
+    unlink("/dev/.booting");
+
     return 0;
 }
 
@@ -708,6 +712,9 @@
     mount("proc", "/proc", "proc", 0, NULL);
     mount("sysfs", "/sys", "sysfs", 0, NULL);
 
+        /* indicate that booting is in progress to background fw loaders, etc */
+    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));
+
         /* We must have some place other than / to create the
          * device nodes for kmsg and null, otherwise we won't
          * be able to remount / read-only later on.
diff --git a/init/ueventd.c b/init/ueventd.c
index 0e97be7..1328d19 100644
--- a/init/ueventd.c
+++ b/init/ueventd.c
@@ -20,6 +20,8 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <ctype.h>
+#include <signal.h>
+
 #include <private/android_filesystem_config.h>
 
 #include "ueventd.h"
@@ -37,6 +39,13 @@
     int nr;
     char tmp[32];
 
+        /* Prevent fire-and-forget children from becoming zombies.
+         * If we should need to wait() for some children in the future
+         * (as opposed to none right now), double-forking here instead
+         * of ignoring SIGCHLD may be the better solution.
+         */
+    signal(SIGCHLD, SIG_IGN);
+
     open_devnull_stdio();
     log_init();