summaryrefslogtreecommitdiffstats
path: root/init
diff options
context:
space:
mode:
authorAndrew F. Davis2018-07-09 13:12:00 -0500
committerAndrew F. Davis2018-07-26 09:42:22 -0500
commit9963847419f41c76ca008cf0c09e986c79f04e4c (patch)
tree2fb79ff1958c42e00c9c511452ffa3a505d84ab5 /init
parentf6ee8fe5155557bf6b796038ef982a9d03c99c07 (diff)
downloadplatform-system-core-9963847419f41c76ca008cf0c09e986c79f04e4c.tar.gz
platform-system-core-9963847419f41c76ca008cf0c09e986c79f04e4c.tar.xz
platform-system-core-9963847419f41c76ca008cf0c09e986c79f04e4c.zip
ueventd: Add dynamic kernel module loading
For some platforms it is not known at build time what devices will be attached at runtime. Building into the kernel or pre-loading at init all the modules that could be needed would unnecessary bloat the kernel. The solution is dynamic kernel module loading. The kernel will generate uevents when devices are added, userspace should monitor for these events and load the compatible modules. The init process already monitors for uevents, add here the ability to respond to modalias events and preform the correct action. Adding this to init is preferred over an external program as we can read and process the module alias and dependency files once, instead of for each module needing to be loaded. Test: Run on Beagle-X15, check all needed modules are loaded (lsmod) Change-Id: I1b57d9aeb0a9770f309207183dc4bc2b7b905f14 Signed-off-by: Andrew F. Davis <afd@ti.com> Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
Diffstat (limited to 'init')
-rw-r--r--init/Android.bp1
-rw-r--r--init/modalias_handler.cpp163
-rw-r--r--init/modalias_handler.h48
-rw-r--r--init/uevent.h1
-rw-r--r--init/uevent_listener.cpp4
-rw-r--r--init/ueventd.cpp13
6 files changed, 227 insertions, 3 deletions
diff --git a/init/Android.bp b/init/Android.bp
index c02b8d187..660d58676 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -108,6 +108,7 @@ cc_library_static {
108 "import_parser.cpp", 108 "import_parser.cpp",
109 "init.cpp", 109 "init.cpp",
110 "keychords.cpp", 110 "keychords.cpp",
111 "modalias_handler.cpp",
111 "parser.cpp", 112 "parser.cpp",
112 "persistent_properties.cpp", 113 "persistent_properties.cpp",
113 "persistent_properties.proto", 114 "persistent_properties.proto",
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
new file mode 100644
index 000000000..1734a7e0f
--- /dev/null
+++ b/init/modalias_handler.cpp
@@ -0,0 +1,163 @@
1/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "modalias_handler.h"
18
19#include <fnmatch.h>
20#include <sys/syscall.h>
21
22#include <algorithm>
23#include <functional>
24#include <string>
25#include <vector>
26
27#include <android-base/chrono_utils.h>
28#include <android-base/logging.h>
29#include <android-base/unique_fd.h>
30
31#include "parser.h"
32
33namespace android {
34namespace init {
35
36Result<Success> ModaliasHandler::ParseDepCallback(std::vector<std::string>&& args) {
37 std::vector<std::string> deps;
38
39 // Set first item as our modules path
40 std::string::size_type pos = args[0].find(':');
41 if (pos != std::string::npos) {
42 deps.emplace_back(args[0].substr(0, pos));
43 } else {
44 return Error() << "dependency lines must start with name followed by ':'";
45 }
46
47 // Remaining items are dependencies of our module
48 for (auto arg = args.begin() + 1; arg != args.end(); ++arg) {
49 deps.push_back(*arg);
50 }
51
52 // Key is striped module name to match names in alias file
53 std::size_t start = args[0].find_last_of("/");
54 std::size_t end = args[0].find(".ko:");
55 if ((end - start) <= 1) return Error() << "malformed dependency line";
56 auto mod_name = args[0].substr(start + 1, (end - start) - 1);
57 // module names can have '-', but their file names will have '_'
58 std::replace(mod_name.begin(), mod_name.end(), '-', '_');
59 this->module_deps_[mod_name] = deps;
60
61 return Success();
62}
63
64Result<Success> ModaliasHandler::ParseAliasCallback(std::vector<std::string>&& args) {
65 auto it = args.begin();
66 const std::string& type = *it++;
67
68 if (type != "alias") {
69 return Error() << "we only handle alias lines, got: " << type;
70 }
71
72 if (args.size() != 3) {
73 return Error() << "alias lines must have 3 entries";
74 }
75
76 std::string& alias = *it++;
77 std::string& module_name = *it++;
78 this->module_aliases_.emplace_back(alias, module_name);
79
80 return Success();
81}
82
83ModaliasHandler::ModaliasHandler() {
84 using namespace std::placeholders;
85
86 static const std::string base_paths[] = {
87 "/vendor/lib/modules/",
88 "/lib/modules/",
89 "/odm/lib/modules/",
90 };
91
92 Parser alias_parser;
93 auto alias_callback = std::bind(&ModaliasHandler::ParseAliasCallback, this, _1);
94 alias_parser.AddSingleLineParser("alias", alias_callback);
95 for (const auto& base_path : base_paths) alias_parser.ParseConfig(base_path + "modules.alias");
96
97 Parser dep_parser;
98 auto dep_callback = std::bind(&ModaliasHandler::ParseDepCallback, this, _1);
99 dep_parser.AddSingleLineParser("", dep_callback);
100 for (const auto& base_path : base_paths) dep_parser.ParseConfig(base_path + "modules.dep");
101}
102
103Result<Success> ModaliasHandler::Insmod(const std::string& path_name, const std::string& args) {
104 base::unique_fd fd(
105 TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
106 if (fd == -1) return ErrnoError() << "Could not open module '" << path_name << "'";
107
108 int ret = syscall(__NR_finit_module, fd.get(), args.c_str(), 0);
109 if (ret != 0) {
110 if (errno == EEXIST) {
111 // Module already loaded
112 return Success();
113 }
114 return ErrnoError() << "Failed to insmod '" << path_name << "' with args '" << args << "'";
115 }
116
117 LOG(INFO) << "Loaded kernel module " << path_name;
118 return Success();
119}
120
121Result<Success> ModaliasHandler::InsmodWithDeps(const std::string& module_name,
122 const std::string& args) {
123 if (module_name.empty()) {
124 return Error() << "Need valid module name";
125 }
126
127 auto it = module_deps_.find(module_name);
128 if (it == module_deps_.end()) {
129 return Error() << "Module '" << module_name << "' not in dependency file";
130 }
131 auto& dependencies = it->second;
132
133 // load module dependencies in reverse order
134 for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {
135 if (auto result = Insmod(*dep, ""); !result) return result;
136 }
137
138 // load target module itself with args
139 return Insmod(dependencies[0], args);
140}
141
142void ModaliasHandler::HandleModaliasEvent(const Uevent& uevent) {
143 if (uevent.modalias.empty()) return;
144
145 for (const auto& [alias, module] : module_aliases_) {
146 if (fnmatch(alias.c_str(), uevent.modalias.c_str(), 0) != 0) continue; // Keep looking
147
148 LOG(DEBUG) << "Loading kernel module '" << module << "' for alias '" << uevent.modalias
149 << "'";
150
151 if (auto result = InsmodWithDeps(module, ""); !result) {
152 LOG(ERROR) << "Cannot load module: " << result.error();
153 // try another one since there may be another match
154 continue;
155 }
156
157 // loading was successful
158 return;
159 }
160}
161
162} // namespace init
163} // namespace android
diff --git a/init/modalias_handler.h b/init/modalias_handler.h
new file mode 100644
index 000000000..e79da3275
--- /dev/null
+++ b/init/modalias_handler.h
@@ -0,0 +1,48 @@
1/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include "result.h"
20#include "uevent.h"
21
22#include <string>
23#include <unordered_map>
24#include <vector>
25
26namespace android {
27namespace init {
28
29class ModaliasHandler {
30 public:
31 ModaliasHandler();
32 ~ModaliasHandler(){};
33
34 void HandleModaliasEvent(const Uevent& uevent);
35
36 private:
37 Result<Success> InsmodWithDeps(const std::string& module_name, const std::string& args);
38 Result<Success> Insmod(const std::string& path_name, const std::string& args);
39
40 Result<Success> ParseDepCallback(std::vector<std::string>&& args);
41 Result<Success> ParseAliasCallback(std::vector<std::string>&& args);
42
43 std::vector<std::pair<std::string, std::string>> module_aliases_;
44 std::unordered_map<std::string, std::vector<std::string>> module_deps_;
45};
46
47} // namespace init
48} // namespace android
diff --git a/init/uevent.h b/init/uevent.h
index c4fd9454c..dc35fd968 100644
--- a/init/uevent.h
+++ b/init/uevent.h
@@ -29,6 +29,7 @@ struct Uevent {
29 std::string firmware; 29 std::string firmware;
30 std::string partition_name; 30 std::string partition_name;
31 std::string device_name; 31 std::string device_name;
32 std::string modalias;
32 int partition_num; 33 int partition_num;
33 int major; 34 int major;
34 int minor; 35 int minor;
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 24b14c44d..8cf212867 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -39,6 +39,7 @@ static void ParseEvent(const char* msg, Uevent* uevent) {
39 uevent->firmware.clear(); 39 uevent->firmware.clear();
40 uevent->partition_name.clear(); 40 uevent->partition_name.clear();
41 uevent->device_name.clear(); 41 uevent->device_name.clear();
42 uevent->modalias.clear();
42 // currently ignoring SEQNUM 43 // currently ignoring SEQNUM
43 while (*msg) { 44 while (*msg) {
44 if (!strncmp(msg, "ACTION=", 7)) { 45 if (!strncmp(msg, "ACTION=", 7)) {
@@ -68,6 +69,9 @@ static void ParseEvent(const char* msg, Uevent* uevent) {
68 } else if (!strncmp(msg, "DEVNAME=", 8)) { 69 } else if (!strncmp(msg, "DEVNAME=", 8)) {
69 msg += 8; 70 msg += 8;
70 uevent->device_name = msg; 71 uevent->device_name = msg;
72 } else if (!strncmp(msg, "MODALIAS=", 9)) {
73 msg += 9;
74 uevent->modalias = msg;
71 } 75 }
72 76
73 // advance to after the next \0 77 // advance to after the next \0
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index cd45a3fb7..e9d829b51 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -36,6 +36,7 @@
36 36
37#include "devices.h" 37#include "devices.h"
38#include "firmware_handler.h" 38#include "firmware_handler.h"
39#include "modalias_handler.h"
39#include "selinux.h" 40#include "selinux.h"
40#include "uevent_listener.h" 41#include "uevent_listener.h"
41#include "ueventd_parser.h" 42#include "ueventd_parser.h"
@@ -106,9 +107,11 @@ namespace init {
106 107
107class ColdBoot { 108class ColdBoot {
108 public: 109 public:
109 ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler) 110 ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler,
111 ModaliasHandler& modalias_handler)
110 : uevent_listener_(uevent_listener), 112 : uevent_listener_(uevent_listener),
111 device_handler_(device_handler), 113 device_handler_(device_handler),
114 modalias_handler_(modalias_handler),
112 num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {} 115 num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
113 116
114 void Run(); 117 void Run();
@@ -122,6 +125,7 @@ class ColdBoot {
122 125
123 UeventListener& uevent_listener_; 126 UeventListener& uevent_listener_;
124 DeviceHandler& device_handler_; 127 DeviceHandler& device_handler_;
128 ModaliasHandler& modalias_handler_;
125 129
126 unsigned int num_handler_subprocesses_; 130 unsigned int num_handler_subprocesses_;
127 std::vector<Uevent> uevent_queue_; 131 std::vector<Uevent> uevent_queue_;
@@ -133,6 +137,7 @@ void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_pr
133 for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) { 137 for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
134 auto& uevent = uevent_queue_[i]; 138 auto& uevent = uevent_queue_[i];
135 device_handler_.HandleDeviceEvent(uevent); 139 device_handler_.HandleDeviceEvent(uevent);
140 modalias_handler_.HandleModaliasEvent(uevent);
136 } 141 }
137 _exit(EXIT_SUCCESS); 142 _exit(EXIT_SUCCESS);
138} 143}
@@ -230,6 +235,7 @@ int ueventd_main(int argc, char** argv) {
230 SelabelInitialize(); 235 SelabelInitialize();
231 236
232 DeviceHandler device_handler; 237 DeviceHandler device_handler;
238 ModaliasHandler modalias_handler;
233 UeventListener uevent_listener; 239 UeventListener uevent_listener;
234 240
235 { 241 {
@@ -251,7 +257,7 @@ int ueventd_main(int argc, char** argv) {
251 } 257 }
252 258
253 if (access(COLDBOOT_DONE, F_OK) != 0) { 259 if (access(COLDBOOT_DONE, F_OK) != 0) {
254 ColdBoot cold_boot(uevent_listener, device_handler); 260 ColdBoot cold_boot(uevent_listener, device_handler, modalias_handler);
255 cold_boot.Run(); 261 cold_boot.Run();
256 } 262 }
257 263
@@ -262,8 +268,9 @@ int ueventd_main(int argc, char** argv) {
262 while (waitpid(-1, nullptr, WNOHANG) > 0) { 268 while (waitpid(-1, nullptr, WNOHANG) > 0) {
263 } 269 }
264 270
265 uevent_listener.Poll([&device_handler](const Uevent& uevent) { 271 uevent_listener.Poll([&device_handler, &modalias_handler](const Uevent& uevent) {
266 HandleFirmwareEvent(uevent); 272 HandleFirmwareEvent(uevent);
273 modalias_handler.HandleModaliasEvent(uevent);
267 device_handler.HandleDeviceEvent(uevent); 274 device_handler.HandleDeviceEvent(uevent);
268 return ListenerAction::kContinue; 275 return ListenerAction::kContinue;
269 }); 276 });