blob: 0dad287b74013a35179a2adf0e3d8b6969cec4d1 [file] [log] [blame]
Austin Schuh608514f2022-12-30 15:51:30 -08001#include <linux/securebits.h>
2#include <pwd.h>
Stephan Pleinesf581a072024-05-23 20:59:27 -07003#include <sched.h>
4#include <stdint.h>
Austin Schuh608514f2022-12-30 15:51:30 -08005#include <sys/prctl.h>
Austin Schuha63163e2023-02-24 16:18:18 -08006#include <sys/resource.h>
Stephan Pleinesf581a072024-05-23 20:59:27 -07007#include <unistd.h>
Austin Schuh608514f2022-12-30 15:51:30 -08008
Stephan Pleinesf581a072024-05-23 20:59:27 -07009#include <algorithm>
10#include <map>
11#include <optional>
12#include <ostream>
13#include <string>
14#include <string_view>
15#include <utility>
16#include <vector>
17
Austin Schuh99f7c6a2024-06-25 22:07:44 -070018#include "absl/flags/flag.h"
19#include "absl/log/check.h"
20#include "absl/log/log.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070021#include "absl/strings/str_cat.h"
22#include "flatbuffers/buffer.h"
23#include "flatbuffers/string.h"
24#include "flatbuffers/vector.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070025
Stephan Pleinesf581a072024-05-23 20:59:27 -070026#include "aos/configuration.h"
27#include "aos/events/event_loop.h"
Austin Schuh608514f2022-12-30 15:51:30 -080028#include "aos/events/shm_event_loop.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070029#include "aos/flatbuffers.h"
Austin Schuh608514f2022-12-30 15:51:30 -080030#include "aos/init.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070031#include "aos/json_to_flatbuffer.h"
Austin Schuh608514f2022-12-30 15:51:30 -080032#include "aos/starter/irq_affinity_lib.h"
33#include "aos/starter/kthread_generated.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070034#include "aos/util/file.h"
Austin Schuh608514f2022-12-30 15:51:30 -080035#include "aos/util/top.h"
Austin Schuh608514f2022-12-30 15:51:30 -080036
Austin Schuh99f7c6a2024-06-25 22:07:44 -070037ABSL_FLAG(std::string, config, "aos_config.json",
38 "File path of aos configuration");
Austin Schuh608514f2022-12-30 15:51:30 -080039
Austin Schuh99f7c6a2024-06-25 22:07:44 -070040ABSL_FLAG(std::string, user, "",
41 "Starter runs as though this user ran a SUID binary if set.");
42ABSL_FLAG(std::string, irq_config, "rockpi_config.json",
43 "File path of rockpi configuration");
Austin Schuh608514f2022-12-30 15:51:30 -080044
45namespace aos {
46
47cpu_set_t AffinityFromFlatbuffer(const flatbuffers::Vector<uint8_t> *v) {
48 cpu_set_t affinity;
49 CPU_ZERO(&affinity);
50 if (v == nullptr) {
51 for (int i = 0; i < CPU_SETSIZE; ++i) {
52 CPU_SET(i, &affinity);
53 }
54 } else {
55 for (uint8_t cpu : *v) {
56 CPU_SET(cpu, &affinity);
57 }
58 }
59 return affinity;
60}
61
62// Class to hold the configuration for an IRQ.
63struct ParsedIrqConfig {
64 std::string name;
65 cpu_set_t affinity;
66
67 void ConfigureIrq(int interrupt_number) const {
68 const std::string affinity_filename =
69 absl::StrCat("/proc/irq/", interrupt_number, "/smp_affinity");
70 const std::string contents = util::ReadFileToStringOrDie(affinity_filename);
71
72 std::string new_contents = std::string(contents.size() - 1, '0');
73
74 // Contents will be a padded string which is the size of the number of
75 // IRQs.
76 CHECK(!(CPU_SETSIZE & 0xf));
77 for (size_t i = 0; i < CPU_SETSIZE; i += 4) {
78 if (i / 4 >= new_contents.size()) {
79 break;
80 }
81 uint8_t byte = 0;
82 if (CPU_ISSET(i + 0, &affinity)) {
83 byte |= 1;
84 }
85 if (CPU_ISSET(i + 1, &affinity)) {
86 byte |= 2;
87 }
88 if (CPU_ISSET(i + 2, &affinity)) {
89 byte |= 4;
90 }
91 if (CPU_ISSET(i + 3, &affinity)) {
92 byte |= 8;
93 }
94 if (byte < 10) {
95 new_contents[new_contents.size() - 1 - i / 4] = '0' + byte;
96 } else {
97 new_contents[new_contents.size() - 1 - i / 4] = 'a' + (byte - 10);
98 }
99 }
100
101 if (contents != new_contents) {
102 util::WriteStringToFileOrDie(affinity_filename, new_contents);
103 }
104 }
105};
106
107// Class to hold the configuration for a kthread.
108struct ParsedKThreadConfig {
109 bool full_match = false;
110 std::string prefix;
111 std::string postfix;
112 starter::Scheduler scheduler;
113 int priority;
Austin Schuha63163e2023-02-24 16:18:18 -0800114 std::optional<int> nice;
Austin Schuh608514f2022-12-30 15:51:30 -0800115 cpu_set_t affinity;
116
117 bool Matches(std::string_view candidate) const {
118 if (full_match) {
119 return candidate == prefix;
120 } else {
121 if (candidate.size() < prefix.size() + postfix.size()) {
122 return false;
123 }
124 if (candidate.substr(0, prefix.size()) != prefix) {
125 return false;
126 }
127 if (candidate.substr(candidate.size() - postfix.size(), postfix.size()) !=
128 postfix) {
129 return false;
130 }
131 return true;
132 }
133 }
134
Austin Schuh19731b02024-03-02 16:53:19 -0800135 void ConfigurePid(pid_t pid, std::string_view name) const {
Austin Schuha63163e2023-02-24 16:18:18 -0800136 struct sched_param param;
137 param.sched_priority = priority;
138 int new_scheduler;
139 switch (scheduler) {
140 case starter::Scheduler::SCHEDULER_OTHER:
141 new_scheduler = SCHED_OTHER;
142 break;
143 case starter::Scheduler::SCHEDULER_RR:
144 new_scheduler = SCHED_RR;
145 break;
146 case starter::Scheduler::SCHEDULER_FIFO:
147 new_scheduler = SCHED_FIFO;
148 break;
149 default:
150 LOG(FATAL) << "Unknown scheduler";
Austin Schuh608514f2022-12-30 15:51:30 -0800151 }
Austin Schuh19731b02024-03-02 16:53:19 -0800152 PCHECK(sched_setscheduler(pid, new_scheduler, &param) == 0)
153 << ", Failed to set " << name << "(" << pid << ") to "
154 << (new_scheduler == SCHED_OTHER
155 ? "SCHED_OTHER"
156 : (new_scheduler == SCHED_RR ? "SCHED_RR" : "SCHED_FIFO"));
Austin Schuha63163e2023-02-24 16:18:18 -0800157
158 if (scheduler == starter::Scheduler::SCHEDULER_OTHER && nice.has_value()) {
159 PCHECK(setpriority(PRIO_PROCESS, pid, *nice) == 0)
160 << ": Failed to set priority";
161 }
162
Austin Schuh608514f2022-12-30 15:51:30 -0800163 PCHECK(sched_setaffinity(pid, sizeof(affinity), &affinity) == 0);
164 }
165};
166
167// TODO(austin): Clean this up a bit, and maybe we can add some tests.
168class IrqAffinity {
169 public:
170 IrqAffinity(
171 EventLoop *event_loop,
172 const aos::FlatbufferDetachedBuffer<aos::starter::IrqAffinityConfig>
173 &irq_affinity_config)
Maxwell Gumleyb27245f2024-04-11 15:46:22 -0600174 : top_(event_loop, aos::util::Top::TrackThreadsMode::kDisabled,
175 aos::util::Top::TrackPerThreadInfoMode::kDisabled) {
Austin Schuh608514f2022-12-30 15:51:30 -0800176 if (irq_affinity_config.message().has_kthreads()) {
Austin Schuh4ec20622023-03-11 15:11:50 -0800177 PopulateThreads(irq_affinity_config.message().kthreads(), &kthreads_);
178 }
179 if (irq_affinity_config.message().has_threads()) {
180 PopulateThreads(irq_affinity_config.message().threads(), &threads_);
Austin Schuh608514f2022-12-30 15:51:30 -0800181 }
182
183 if (irq_affinity_config.message().has_irqs()) {
184 irqs_.reserve(irq_affinity_config.message().irqs()->size());
185 for (const starter::IrqConfig *irq_config :
186 *irq_affinity_config.message().irqs()) {
187 CHECK(irq_config->has_name()) << ": Name required";
188 LOG(INFO) << "IRQ " << aos::FlatbufferToJson(irq_config);
189 irqs_.push_back(ParsedIrqConfig{
190 .name = irq_config->name()->str(),
191 .affinity = AffinityFromFlatbuffer(irq_config->affinity()),
192 });
193 }
194 }
195
196 top_.set_track_top_processes(true);
197 top_.set_on_reading_update([this]() {
198 for (const std::pair<const pid_t, util::Top::ProcessReadings> &reading :
199 top_.readings()) {
200 if (reading.second.kthread) {
201 for (const ParsedKThreadConfig &match : kthreads_) {
202 if (match.Matches(reading.second.name)) {
Austin Schuh19731b02024-03-02 16:53:19 -0800203 match.ConfigurePid(reading.first, reading.second.name);
Austin Schuh608514f2022-12-30 15:51:30 -0800204 break;
205 }
206 }
Austin Schuh4ec20622023-03-11 15:11:50 -0800207 } else {
208 for (const ParsedKThreadConfig &match : threads_) {
209 if (match.Matches(reading.second.name)) {
Austin Schuh19731b02024-03-02 16:53:19 -0800210 match.ConfigurePid(reading.first, reading.second.name);
Austin Schuh4ec20622023-03-11 15:11:50 -0800211 break;
212 }
213 }
Austin Schuh608514f2022-12-30 15:51:30 -0800214 }
215 }
216
217 interrupts_status_.Update();
218
219 for (const InterruptsStatus::InterruptState &state :
220 interrupts_status_.states()) {
221 for (const ParsedIrqConfig &match : irqs_) {
222 bool matched = false;
223 for (const std::string &action : state.actions) {
224 if (match.name == action) {
225 matched = true;
226 break;
227 }
228 }
229 if (matched) {
230 match.ConfigureIrq(state.interrupt_number);
231 }
232 }
233 }
234 });
235 }
236
237 private:
Austin Schuh4ec20622023-03-11 15:11:50 -0800238 void PopulateThreads(
239 const flatbuffers::Vector<flatbuffers::Offset<starter::KthreadConfig>>
240 *threads_config,
241 std::vector<ParsedKThreadConfig> *threads) {
242 threads->reserve(threads_config->size());
243 for (const starter::KthreadConfig *kthread_config : *threads_config) {
244 LOG(INFO) << "Kthread " << aos::FlatbufferToJson(kthread_config);
245 CHECK(kthread_config->has_name()) << ": Name required";
246 const size_t star_position =
247 kthread_config->name()->string_view().find('*');
248 const bool has_star = star_position != std::string_view::npos;
249
250 threads->push_back(ParsedKThreadConfig{
251 .full_match = !has_star,
252 .prefix = std::string(
253 !has_star ? kthread_config->name()->string_view()
254 : kthread_config->name()->string_view().substr(
255 0, star_position)),
256 .postfix = std::string(
257 !has_star ? ""
258 : kthread_config->name()->string_view().substr(
259 star_position + 1)),
260 .scheduler = kthread_config->scheduler(),
261 .priority = kthread_config->priority(),
262 .nice = kthread_config->nice(),
263 .affinity = AffinityFromFlatbuffer(kthread_config->affinity()),
264 });
265 }
266 }
267
Austin Schuh608514f2022-12-30 15:51:30 -0800268 util::Top top_;
269
270 // TODO(austin): Publish message with everything in it.
271 // TODO(austin): Make Top report out affinity + priority + scheduler for
272 // posterity.
273
274 std::vector<ParsedKThreadConfig> kthreads_;
Austin Schuh4ec20622023-03-11 15:11:50 -0800275 std::vector<ParsedKThreadConfig> threads_;
Austin Schuh608514f2022-12-30 15:51:30 -0800276 std::vector<ParsedIrqConfig> irqs_;
277
278 InterruptsStatus interrupts_status_;
279};
280
281} // namespace aos
282
283int main(int argc, char **argv) {
284 aos::InitGoogle(&argc, &argv);
285
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700286 if (!absl::GetFlag(FLAGS_user).empty()) {
Austin Schuh608514f2022-12-30 15:51:30 -0800287 // Maintain root permissions as we switch to become the user so we can
288 // actually manipulate priorities.
289 PCHECK(prctl(PR_SET_SECUREBITS, SECBIT_NO_SETUID_FIXUP | SECBIT_NOROOT) ==
290 0);
291
292 uid_t uid;
293 uid_t gid;
294 {
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700295 struct passwd *user_data = getpwnam(absl::GetFlag(FLAGS_user).c_str());
Austin Schuh608514f2022-12-30 15:51:30 -0800296 if (user_data != nullptr) {
297 uid = user_data->pw_uid;
298 gid = user_data->pw_gid;
299 } else {
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700300 LOG(FATAL) << "Could not find user " << absl::GetFlag(FLAGS_user);
Austin Schuh608514f2022-12-30 15:51:30 -0800301 return 1;
302 }
303 }
304 // Change the real and effective IDs to the user we're running as. The
305 // effective IDs mean files we access (like shared memory) will happen as
306 // that user. The real IDs allow child processes with an different effective
307 // ID to still participate in signal sending/receiving.
308 constexpr int kUnchanged = -1;
309 if (setresgid(/* ruid */ gid, /* euid */ gid,
310 /* suid */ kUnchanged) != 0) {
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700311 PLOG(FATAL) << "Failed to change GID to " << absl::GetFlag(FLAGS_user);
Austin Schuh608514f2022-12-30 15:51:30 -0800312 }
313
314 if (setresuid(/* ruid */ uid, /* euid */ uid,
315 /* suid */ kUnchanged) != 0) {
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700316 PLOG(FATAL) << "Failed to change UID to " << absl::GetFlag(FLAGS_user);
Austin Schuh608514f2022-12-30 15:51:30 -0800317 }
318 }
319
320 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700321 aos::configuration::ReadConfig(absl::GetFlag(FLAGS_config));
Austin Schuh608514f2022-12-30 15:51:30 -0800322
Austin Schuh608514f2022-12-30 15:51:30 -0800323 aos::FlatbufferDetachedBuffer<aos::starter::IrqAffinityConfig>
324 irq_affinity_config =
Alexander Yeee61cac32023-02-11 19:40:40 -0800325 aos::JsonFileToFlatbuffer<aos::starter::IrqAffinityConfig>(
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700326 absl::GetFlag(FLAGS_irq_config));
Austin Schuh608514f2022-12-30 15:51:30 -0800327
328 aos::ShmEventLoop shm_event_loop(&config.message());
329
330 aos::IrqAffinity irq_affinity(&shm_event_loop, irq_affinity_config);
331
332 shm_event_loop.Run();
333
334 return 0;
335}