android_system_core/init/firmware_handler.cpp
Tom Cherry 0f296e06d6 ueventd: don't double fork firmware handlers
ueventd may be asked to handle firmware during the time critical
coldboot process.  If we double fork to avoid needing to reap the
firmware handler, then we may add significant delay to this process,
as the first child may not get scheduled quickly enough for waitpid()
to complete without delay.

Bug: 63081260
Test: boot bullhead and sailfish, check that firmwares are loaded,
      no zombie ueventd processes remain, and no new errors are shown
Change-Id: I2bac3b1fbc3a58557a00326e491c104656db27ae
2017-07-05 16:41:11 -07:00

126 lines
3.9 KiB
C++

/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "firmware_handler.h"
#include <fcntl.h>
#include <sys/sendfile.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include <thread>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include "util.h"
using android::base::unique_fd;
using android::base::WriteFully;
namespace android {
namespace init {
static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
int loading_fd, int data_fd) {
// Start transfer.
WriteFully(loading_fd, "1", 1);
// Copy the firmware.
int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
if (rc == -1) {
PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware
<< "' }";
}
// Tell the firmware whether to abort or commit.
const char* response = (rc != -1) ? "0" : "-1";
WriteFully(loading_fd, response, strlen(response));
}
static bool IsBooting() {
return access("/dev/.booting", F_OK) == 0;
}
static void ProcessFirmwareEvent(const Uevent& uevent) {
int booting = IsBooting();
LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
std::string root = "/sys" + uevent.path;
std::string loading = root + "/loading";
std::string data = root + "/data";
unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
if (loading_fd == -1) {
PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
return;
}
unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
if (data_fd == -1) {
PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
return;
}
static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
"/firmware/image/"};
try_loading_again:
for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
std::string file = firmware_dirs[i] + uevent.firmware;
unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
struct stat sb;
if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
return;
}
}
if (booting) {
// If we're not fully booted, we may be missing
// filesystems needed for firmware, wait and retry.
std::this_thread::sleep_for(100ms);
booting = IsBooting();
goto try_loading_again;
}
LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
// Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
write(loading_fd, "-1", 2);
}
void HandleFirmwareEvent(const Uevent& uevent) {
if (uevent.subsystem != "firmware" || uevent.action != "add") return;
// Loading the firmware in a child means we can do that in parallel...
auto pid = fork();
if (pid == -1) {
PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
}
if (pid == 0) {
Timer t;
ProcessFirmwareEvent(uevent);
LOG(INFO) << "loading " << uevent.path << " took " << t;
_exit(EXIT_SUCCESS);
}
}
} // namespace init
} // namespace android