Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d145d8a authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I22edd5b8,Ide510584,I4b395c48,Ie51344b0,I0d4e3e75 into main

* changes:
  ueventd: allow the ueventd main loop to run in parallel
  ueventd: introduce UeventDependencyGraph class
  ueventd: allow FirmwareHandler to run in a single process after coldboot
  ueventd: make some FirmwareHandler fields const
  ueventd: parse seqnum field of a uevent
parents c4e368b8 b3eb194c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ init_device_sources = [
    "switch_root.cpp",
    "tradeinmode.cpp",
    "uevent_listener.cpp",
    "uevent_dependency_graph.cpp",
    "ueventd.cpp",
    "ueventd_parser.cpp",
]
@@ -513,6 +514,8 @@ cc_test {
        "service_test.cpp",
        "subcontext_test.cpp",
        "tokenizer_test.cpp",
        "uevent_dependency_graph.cpp",
        "uevent_dependency_graph_test.cpp",
        "ueventd_parser_test.cpp",
        "ueventd_test.cpp",
        "util_test.cpp",
+28 −0
Original line number Diff line number Diff line
@@ -211,3 +211,31 @@ For example
    parallel_restorecon_dir /sys/devices
    parallel_restorecon_dir /sys/devices/platform
    parallel_restorecon_dir /sys/devices/platform/soc

## Parallel uevent main loop
--------
After coldboot is complete, ueventd enters its main loop. The main loop handles all uevents that
happen after coldboot. This main loop is not parallelized by default unlike the coldboot process
described above. You can optionally parallelize this main loop by.

    parallel_ueventd_main_loop enabled

By default this spawns the same number of threads as the number of the logical cores. You can
optionally tweak the number of workers.

    parallel_ueventd_main_loop_max_workers 2

There are two motivations you might want to try parallelizing the main loop; boot time and kernel
module initialization time.

The main loop handles events that occur when you modify device states (e.g. plug/unplug a new
device), but it also processes uevents necessary for the boot process such as uevents related to
`/data` partitions. These uevents block the boot process, so parallelizing the main loop might help
the boot time for the same reason as parallel restorecon does in coldboot (e.g. labeling sysfs nodes
that cannot be migrated to genfscon).

Some kernel modules take a perceptible time for initialization. If a device is supposed to
initialize multiple number of such kernel modules, parallelizing them reduces the total
initialization time. In addition, these kernel module initializations can block following events for
`/data` block devices until they complete. In that case, this also contributes to making the boot
time faster.
+31 −13
Original line number Diff line number Diff line
@@ -134,9 +134,17 @@ ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
    : ExternalFirmwareHandler(devpath, uid, 0, handler_path) {}

FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories,
                                 std::vector<ExternalFirmwareHandler> external_firmware_handlers)
                                 std::vector<ExternalFirmwareHandler> external_firmware_handlers,
                                 bool serial_handler_after_coldboot)
    : firmware_directories_(std::move(firmware_directories)),
      external_firmware_handlers_(std::move(external_firmware_handlers)) {}
      external_firmware_handlers_(std::move(external_firmware_handlers)),
      serial_handler_after_coldboot_(serial_handler_after_coldboot) {}

void FirmwareHandler::ColdbootDone() {
    if (serial_handler_after_coldboot_) {
        enables_parallel_handlers_ = false;
    }
}

std::string FirmwareHandler::GetFirmwarePath(const Uevent& uevent) const {
    for (const auto& external_handler : external_firmware_handlers_) {
@@ -265,21 +273,31 @@ bool FirmwareHandler::ForEachFirmwareDirectory(
    return false;
}

void FirmwareHandler::HandleUeventInternal(const Uevent& uevent) const {
    Timer t;
    auto firmware = GetFirmwarePath(uevent);
    ProcessFirmwareEvent(uevent.path, firmware);
    LOG(INFO) << "loading " << uevent.path << " took " << t;
}

void FirmwareHandler::HandleUevent(const Uevent& uevent) {
    if (uevent.subsystem != "firmware" || uevent.action != "add") return;

    if (enables_parallel_handlers_) {
        // 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;
        auto firmware = GetFirmwarePath(uevent);
        ProcessFirmwareEvent(uevent.path, firmware);
        LOG(INFO) << "loading " << uevent.path << " took " << t;
        } else if (pid == 0) {
            // Child does the actual work
            HandleUeventInternal(uevent);
            _exit(EXIT_SUCCESS);
        } else {
            // The main process returns here. Let the child do the actual work in parallel.
            return;
        }
    }
    HandleUeventInternal(uevent);
}

}  // namespace init
+8 −3
Original line number Diff line number Diff line
@@ -45,21 +45,26 @@ struct ExternalFirmwareHandler {
class FirmwareHandler : public UeventHandler {
  public:
    FirmwareHandler(std::vector<std::string> firmware_directories,
                    std::vector<ExternalFirmwareHandler> external_firmware_handlers);
                    std::vector<ExternalFirmwareHandler> external_firmware_handlers,
                    bool serial_handler_after_coldboot);
    virtual ~FirmwareHandler() = default;

    void HandleUevent(const Uevent& uevent) override;
    void ColdbootDone() override;

  private:
    friend void FirmwareTestWithExternalHandler(const std::string& test_name,
                                                bool expect_new_firmware);
    void HandleUeventInternal(const Uevent& uevent) const;

    std::string GetFirmwarePath(const Uevent& uevent) const;
    void ProcessFirmwareEvent(const std::string& path, const std::string& firmware) const;
    bool ForEachFirmwareDirectory(std::function<bool(const std::string&)> handler) const;

    std::vector<std::string> firmware_directories_;
    std::vector<ExternalFirmwareHandler> external_firmware_handlers_;
    std::atomic_bool enables_parallel_handlers_ = true;
    const std::vector<std::string> firmware_directories_;
    const std::vector<ExternalFirmwareHandler> external_firmware_handlers_;
    const bool serial_handler_after_coldboot_ = true;
};

}  // namespace init
+2 −1
Original line number Diff line number Diff line
@@ -35,7 +35,8 @@ void FirmwareTestWithExternalHandler(const std::string& test_name, bool expect_n
    auto external_firmware_handler = ExternalFirmwareHandler(
            "/devices/led/firmware/test_firmware001.bin", getuid(), test_path);

    auto firmware_handler = FirmwareHandler({"/test"}, {external_firmware_handler});
    auto firmware_handler = FirmwareHandler({"/test"}, {external_firmware_handler},
                                            /*serial_handler_after_cold_boot=*/false);

    auto uevent = Uevent{
            .path = "/devices/led/firmware/test_firmware001.bin",
Loading